JWT身份验证,带弹簧和带空头的角度



我正在尝试使用spring-boot和angular进行JWT令牌身份验证。在创建登录承载令牌之后,但在JWTAuthorizationFilter中,我得到了null标头,因此它返回anonymousUser。请告诉我为什么我得到空头。

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);
}
}

JWTA授权过滤器.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);
}
}

Userinfo.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

请告诉我我在这里缺了什么。

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

SecurityContextHolder.getContext().setAuthentication(authResult);

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

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

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

最新更新