如果我手动设置身份验证,则Principle的自动注入将为null

时间:2019-05-05 14:16:57

标签: java spring-security

我试图在自己的登录控制器中自定义登录过程,而不是使用UsernamePasswordAuthenticationFilter

 @PostMapping(value = "/login")
    public ResponseEntity<?> login(
            HttpServletRequest httpRequest,
            @RequestBody AuthenticationRequest authenticationRequest) {
          // authentication code here
         Authentication authenticate=this.authenticationManager.authenticate(authRequest);
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authentication);
        return handlerAuthLogin(httpRequest, result, authorizationRequest);
    }

但是,如果我按如下所示成功登录,则无法在其他控制器中自动注入Principal:

@Controller
public class UsersController {

  @RequestMapping(value = "/me")
  public string getMyName(Principal principal){
    return principal.getName(); // principal is null
  }
}

有谁知道为什么要解决它?

2 个答案:

答案 0 :(得分:0)

执行context.setAuthentication(authentication)时,身份验证仅对当前请求有效。因此,对于第二个/me请求,您还需要设置身份验证。 因此,您需要根据每个请求对用户进行身份验证。这可以通过实现GenericFilterBean来完成:

public class CustomAuthenticationFilter extends GenericFilterBean {

    private final AuthenticationManager authenticationManager;

    public CustomAuthenticationFilter(
            AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;

        /* 
           Note that you need to receive the authentication token in different manner now. 
           Usually headers are used for that.
        */
        Authentication authenticate = authenticationManager.authenticate(request.getHeader("authToken"));
        SecurityContext context = SecurityContextHolder.getContext().setAuthentication(authentication);

        chain.doFilter(request, response);
    }
}

实现过滤器后,您需要将其注册到servlet容器中最合适的位置。 Spring Security根据WebsecutiryConfigurer处理安全过滤器,因此您需要在用户各自的配置器的配置中注册过滤器。

作为示例,我将其放在ConcurrentSessionFilter之后:

@Configuration
@Order(1)
public static class UserWebSecurity extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        CustomAuthenticationFilter filter = new PlayerAuthenticationFilter(jwtService,
                objectMapper);

        http.addFilterAfter(filter, ConcurrentSessionFilter.class);

        (...)
    }

}

查看有关filter ordering的文档,以找到最适合您的方法的位置。

更新

关于这个主题,我写了更深入的blog post。随意检查一下。

答案 1 :(得分:-1)

@Marcus

感谢您的澄清,我找到了造成此案的原因。

我错误地配置了

public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 public WebSecurityConfig() {
     super(true); // I disable the default config, so the SecurityContextPersistenceFilter didn't be added by default and all my SecurityContext info is not persistent
 }
}