【问题标题】:JWT authentication with spring and angular with null headerJWT 认证与弹簧和角度与空头
【发布时间】:2020-07-29 12:43:49
【问题描述】:

我正在尝试使用 Spring Boot 和 Angular 进行 JWT 令牌身份验证。创建登录不记名令牌后,但在 JWTAuthorizationFilter 之后,我得到空头,因此它返回匿名用户。请告诉我为什么我得到空标题。

SecurityConfig.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @Autowired
    private CustomUserDetailService customUserDetailService;
     
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception { 
         http.
         cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues())
        .and().csrf().disable()
                .authorizeRequests()                                                                
                .antMatchers("/**").permitAll()                  
                .antMatchers("/manage/**").hasRole("ADMIN")                                      
                .antMatchers("/").hasRole("USER")
                .and()
                .exceptionHandling()
                .accessDeniedPage("/access-denied")
                .and()
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                .addFilter(new JWTAuthorizationFilter(authenticationManager(), customUserDetailService));
    }
     
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailService).passwordEncoder(new 
   
        BCryptPasswordEncoder());
    }
}

JWTAuthenticationFilter.java

    public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    private AuthenticationManager authenticationManager;

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

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        try {
            UserDetail user = new ObjectMapper().readValue(request.getInputStream(), UserDetail.class);
            return this.authenticationManager
                    .authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), user.getPassword()));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        String username = ((org.springframework.security.core.userdetails.User) authResult.getPrincipal()).getUsername();
        String token = Jwts
                .builder()
                .setSubject(username)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        String bearerToken = TOKEN_PREFIX + token;
        System.out.println(bearerToken);
        response.getWriter().write(bearerToken);
        response.addHeader(HEADER_STRING, bearerToken);
       }
    }

JWTAuthorizationFilter.java

    public class JWTAuthorizationFilter extends BasicAuthenticationFilter {
    private final CustomUserDetailService customUserDetailService;

    public JWTAuthorizationFilter(AuthenticationManager authenticationManager, CustomUserDetailService customUserDetailService) {
        super(authenticationManager);
        this.customUserDetailService = customUserDetailService;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader(HEADER_STRING);
        if (header == null || !header.startsWith(TOKEN_PREFIX)) {
            chain.doFilter(request, response);
            return;
        }
        UsernamePasswordAuthenticationToken authenticationToken = getAuthenticationToken(request);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthenticationToken(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (token == null) return null;
        String username = Jwts.parser().setSigningKey(SECRET)
                .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                .getBody()
                .getSubject();
        UserDetails userDetails = customUserDetailService.loadUserByUsername(username);
        return username != null ?
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) 
      : null;
      }
      }

CustomUserDetailService.java

@Component
public class CustomUserDetailService implements UserDetailsService {

    private List<GrantedAuthority> role;

    @Autowired
    private UserDAO userDAO;

    /*
     * @Autowired public CustomUserDetailService(UserRepository userRepository) {
     * this.userRepository = userRepository; }
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        User user = Optional.ofNullable(userDAO.getByEmail(username))
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        List<GrantedAuthority> authorityListAdmin = AuthorityUtils.createAuthorityList("ROLE_ADMIN");
        List<GrantedAuthority> authorityListUser = AuthorityUtils.createAuthorityList("ROLE_USER");
        if (user.getRole() == "admin") {
            role = authorityListAdmin;
        } else {
            role = authorityListUser;
        }
        return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), role);
    }
}

用户信息.java

private String email;
private String role;
private String password;

控制器

@RequestMapping(value = "/login")
public ModelAndView login(
        @RequestParam(name = "error", required = false) String error,
        @RequestParam(name = "logout", required = false) String logout,
        HttpServletRequest request,
        HttpServletResponse response) {

    ModelAndView mv = new ModelAndView("login");
    HttpSession session= request.getSession(false);
    Authentication auth = SecurityContextHolder.getContext()
        .getAuthentication();
      
    System.out.println("auth ===" + auth);
    System.out.println("logout ===" + logout);
 
    return mv; 
}

这是控制台的输出:

 

 Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJidW50QGdtYWlsLmNvbSIsImV4cCI6MTU5NjExMjcwM30.fBFMDO--8Q_56LT_qbioiT6p3BOxk3L9OrPVTw5EGbf7oJ0ky7W7PuahIYcdjYSL6-OsHY6qq8tPEetlJO7nEg

auth ===org.springframework.security.authentication.AnonymousAuthenticationToken@823df96a: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@b364: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS

请告诉我这里缺少什么。

【问题讨论】:

    标签: java angular spring-boot


    【解决方案1】:

    扩展 UsernamePasswordAuthenticationFilterJWTAuthenticationFilter 会覆盖默认调用此行的 successfulAuthentication 方法:

    SecurityContextHolder.getContext().setAuthentication(authResult);
    

    您的实现没有此行,因此在处理此过滤器后,您在 Spring 上下文中仍然没有 Authentication。调用的下一个过滤器是您的JWTAuthorizationFilter,它尝试从与前一个过滤器相同的request 对象中读取标头。 JWTAuthenticationFilter 将此标头设置为 response 对象而不是 request 对象。所以基本上你最终没有进行身份验证,因为if (header == null || !header.startsWith(TOKEN_PREFIX)) 在登录流程后始终为真。

    【讨论】:

    • 我传递给SecurityContextHolder.getContext().setAuthentication(authResult);authResult对象的isAuthenticated()方法返回了什么?它应该设置为true
    【解决方案2】:

    首先,在 HttpServletResponse 标头上生成和设置的身份验证过滤器令牌中,而不是在请求对象的标头上。然后授权过滤器检查请求头中的令牌,所以可能发生了null的问题。

    通常身份验证和授权不会像这样链接,但不知道您尝试实现的实际用例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-08
      • 2015-11-27
      • 2018-06-12
      • 2023-03-29
      • 1970-01-01
      • 2019-07-03
      • 2015-10-16
      相关资源
      最近更新 更多