我正在基于一些开源项目和网上找到的一些文档,使用 Spring boot 来实现 JWT。
什么在起作用? 我能够生成我的 token ,并且在第一次点击时,当我尝试调用一些安全方法时,我被撤销,这很好。
有什么问题吗? 生成 token 后,似乎我可以调用我的安全方法,而无需添加我的授权 header 。
我正在调试我的代码,发现我在 SecurityContextHolder 中设置了身份验证,但请求完成后我不会清空此变量。在每个实现中都没有人这样做,所以我的问题是我是否必须这样做才能让我的代码按预期工作,仅在存在带有有效 token 的授权 header 时检索安全路径?
我的代码:
WebSecurityConfig 类:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserSecurityService userSecurityService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/public").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated();
// And filter other requests to check the presence of JWT in header
http.addFilterBefore(jwtAuthenticationFilterBean(),
UsernamePasswordAuthenticationFilter.class);
// Disable page caching
http.headers().cacheControl();
}
@Bean
public JWTAuthenticationFilter jwtAuthenticationFilterBean() {
return new JWTAuthenticationFilter();
}
@Override
protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userSecurityService);
}
}
JWTAuthenticationFiler 类
public class JWTAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Value("${jwt.token.header}")
private String tokenHeader;
@Autowired
TokenAuthenticationService tokenAuthenticationService;
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
String token = httpServletRequest.getHeader(tokenHeader);
String username = tokenAuthenticationService.getUsernameFromToken(token);
if(username != null && SecurityContextHolder.getContext().getAuthentication() != null){
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (tokenAuthenticationService.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}
TokenAuthenticationService 类
@Service
public class TokenAuthenticationService implements Serializable {
static final String CLAIM_KEY_USERNAME = "sub";
static final String CLAIM_KEY_AUDIENCE = "audience";
static final String CLAIM_KEY_CREATED = "created";
static final String CLAIM_KEY_EXPIRED = "exp";
static final long EXPIRATIONTIME = 864_000_000; // 10 days
static final String SECRET = "ThisIsASecret";
static final String TOKEN_PREFIX = "Bearer";
static final String HEADER_STRING = "Authorization";
@Value("${jwt.token.expiration}")
private Long expiration;
@Value("${jwt.token.secret}")
private String secret;
public String generateToken(UserDetails user) {
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, user.getUsername());
final Date createdDate = new Date();
claims.put(CLAIM_KEY_CREATED, createdDate);
final Date expirationDate = new Date(createdDate.getTime() + expiration * 1000);
return Jwts.builder()
.setClaims(claims)
.setExpiration(expirationDate)
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
public String getUsernameFromToken(String token) {
String username;
try{
final Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
}catch (Exception e){
username = null;
}
return username;
}
private Claims getClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}catch (Exception e){
claims = null;
}
return claims;
}
public boolean validateToken(String token, UserDetails userDetails) {
final String username = getUsernameFromToken(token);
final Date created = getCreatedDateFromToken(token);
if(userDetails.getUsername().equals(username) && !isTokenExpired(token)){
return true;
}
return false;
}
private boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
private Date getExpirationDateFromToken(String token) {
Date expiration;
try {
final Claims claims = getClaimsFromToken(token);
expiration = claims.getExpiration();
}catch (Exception e){
expiration = null;
}
return expiration;
}
private Date getCreatedDateFromToken(String token) {
Date createdDate;
try{
final Claims claims = getClaimsFromToken(token);
createdDate = new Date((Long) claims.get(CLAIM_KEY_CREATED));
}catch (Exception e){
createdDate = null;
}
return createdDate;
}
}
这是我的 Controller 测试类
@RestController
public class TestController {
@GetMapping("/public")
public String testPublic(){
return "Welcom to the public place";
}
@GetMapping("/private")
@PreAuthorize("hasRole('USER')")
public String testPrivate(){
return "Welcome to the private place";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String testAdmin(){
return "Welcome to the admin place";
}
}
谢谢
最佳答案
所以我的问题的答案需要进行两处修改:
- 我们需要将其添加到我们的 WebSecurityConfig 中来拥有无状态 session 管理策略
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
- 第二件事是我的代码中的拼写错误,我们必须确保在过滤器的条件下身份验证为空:
if(username != null && SecurityContextHolder.getContext().getAuthentication() == null)
关于java - 使用 JWT 完成请求后是否必须清空 SecurityContextHolder,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45001060/