Spring 安全,主体返回 null

Posted

技术标签:

【中文标题】Spring 安全,主体返回 null【英文标题】:Spring security , Principal returns null 【发布时间】:2021-06-06 12:26:42 【问题描述】:

我正在尝试使用 postman 将对象发布到数据库,令牌生成 corectlly 但主体为空!?为什么 在控制器中我有以下代码。 和错误如图 主体 tostring 是:UsernamePasswordAuthenticationToken [Principal=com.accessjobs.pjp.domain.User@1a1e348b, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0 :1,SessionId=null],授予权限=[]] : 主要的 getName 是 : null


@RestController
@RequestMapping("/api/job")
@CrossOrigin //to hit the backend server when connect from the frontend server
public class JobController 

    Logger logger = LoggerFactory.getLogger(JobController.class);

    @Autowired
    private JobService jobService;
    @Autowired
    private MapValidationErrorsService mapValidationErrorsService;

    //@valid annotation to validate the object attributes and , i used the following code FieldErrors to display the list of errors from validation
    @PostMapping("")
    @CrossOrigin
    public ResponseEntity<?> add(@Valid @RequestBody Job job, BindingResult result, Principal principal) 

        ResponseEntity<?> errorMap = mapValidationErrorsService.MapValidationService(result);
        if (errorMap != null) return errorMap;

        logger.info("the principal tostring is : "+principal.toString());
        logger.info("the principal getName is : "+principal.getName());

        Job tempJob = jobService.add(job, principal.getName());
        return new ResponseEntity<Job>(tempJob, HttpStatus.CREATED);
    

如图所示的服务作业代码,总是给我一个空用户并捕获错误。 那是因为电子邮件为空。 解决办法是什么>?


import com.accessjobs.pjp.domain.Job;
import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.exceptions.JobIdException;
import com.accessjobs.pjp.repositories.JobRepository;
import com.accessjobs.pjp.repositories.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Locale;

@Service
public class JobService 
    Logger logger = LoggerFactory.getLogger(JobService.class);

    @Autowired
    private JobRepository jobRepository;
    @Autowired
    private UserRepository userRepository;

    public Job add(Job job,String email)
        //logic , validation and handling
        try 
            logger.info("email .is"+email);
            User user = userRepository.findByEmail(email);
            logger.info("user is ::"+user.toString());
            job.setUser(user);
            logger.warn(user.getEmail());
            job.setUserRole(user.getRole());
            logger.info(user.getRole());
            //convert the identifier to upper case
            job.setJobIdentifier(job.getJobIdentifier().toUpperCase(Locale.ROOT));
            return jobRepository.save(job);
        catch (Exception e)
            throw new JobIdException("Job Identifier '"+job.getJobIdentifier().toUpperCase(Locale.ROOT)+"' already exists");
        
    


package com.accessjobs.pjp.services;

import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.exceptions.EmailAlreadyExistsException;
import com.accessjobs.pjp.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

@Service
public class UserService 
    @Autowired
    private UserRepository userRepository;

    @Autowired //define the bean in the main spring boot app
    private BCryptPasswordEncoder bCryptPasswordEncoder;//store non readable password in the database

    public User register(User newUser)
        try
            newUser.setPassword(bCryptPasswordEncoder.encode(newUser.getPassword()));
            //user name has to be unique (exception)
            newUser.setEmail(newUser.getEmail());
            //make sure that pass and confpass are matches
            //we dont presist or show the confirm password
            newUser.setConfirmPassword("");
            return userRepository.save(newUser);

        catch (Exception e)
            throw new EmailAlreadyExistsException("Email' "+newUser.getEmail()+"' is already exists");
        

    

可能是数据库的错误,我如何允许 mysql 数据库的所有 URL

这是实现用户详情服务的类

package com.accessjobs.pjp.services;


import com.accessjobs.pjp.domain.User;
import com.accessjobs.pjp.repositories.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CustomUserDetailsService implements UserDetailsService 

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String email) throws UsernameNotFoundException 
        User user = userRepository.findByEmail(email);
        if(user==null) new UsernameNotFoundException("User not found");
        return user;
    


    @Transactional
    public User loadUserById(Long id)
        User user = userRepository.getById(id);
        if(user==null) new UsernameNotFoundException("User not found");
        return user;

    

这个类有什么错误吗? 特别是在 MySQL 权限和 antMatchers 方面?

package com.accessjobs.pjp.security;

import com.accessjobs.pjp.services.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import static com.accessjobs.pjp.security.SecurityConstants.*;

//video rep branch 65


@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(
        securedEnabled = true,
        jsr250Enabled = true,
        prePostEnabled = true
)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;
    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() 
        return new JwtAuthenticationFilter();
    

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception 
        authenticationManagerBuilder.userDetailsService(customUserDetailsService).passwordEncoder(bCryptPasswordEncoder);
    

    @Override
    @Bean(BeanIds.AUTHENTICATION_MANAGER)
    protected AuthenticationManager authenticationManager() throws Exception 
        return super.authenticationManager();
    


    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.cors().and().csrf().disable()
                .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // .and().headers().frameOptions().sameOrigin() //To enable H2 Database
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/",
                        "/favicon.ico",
                        "/**/*.png",
                        "/**/*.gif",
                        "/**/*.svg",
                        "/**/*.jpg",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/api/users/**").permitAll()
                .antMatchers(SIGN_UP_URLS).permitAll()
                .antMatchers("jdbc:mysql://localhost:3306/").permitAll()**//here ?**
                .antMatchers("/api/users/**").permitAll()
                .antMatchers("/api/users/**").permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    

【问题讨论】:

请编辑您的问题以添加您的UserDetailsService 实现的源代码。它可能没有从您的用户帐户数据库中读取电子邮件字段。 也许是 Spring Security - 不访问数据库如何允许所有 MySQL db url @ChinHuang 完成 你的 UserService 类没有实现 Spring Security 的UserDetailsService 接口。你有另一个实现 UserDetailsS​​ervice 的类吗? @chinHuang 是的,我在上面添加了它 【参考方案1】:

确保您的令牌具有sub 属性。使用此工具发布您的令牌的内容(减去任何安全属性)。

https://jwt.io/

【讨论】:

它给了我一个 Invalid Signature ,似乎是访问令牌中的错误! @AhmadAmer 很高兴你发现了这一点。您的身份提供者应该生成一个有效的令牌。也许您的 IDP 有错误? 主体中的错误,因为它返回一个空值,我检查了令牌并正确生成了它。如果我通过静态电子邮件传递 prncipal.getNAme() 一切正常

以上是关于Spring 安全,主体返回 null的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot安全主体用户已更改为新登录的用户详细信息

Spring Security 在登录失败时返回 String 作为主体而不是 UserDetails?

Spring Security Oauth2:主体对象未返回扩展的用户详细信息

spring security 匿名访问安全吗

如何控制项目的访问权限来保证项目安全?SpringBoot 集成 Spring Security 安全框架使用指南

Spring security oauth2 - 从 OAuth2 主体获取自定义数据