在使用JWT完成请求后,我们是否必须清空SecurityContextHolder

时间:2017-07-09 21:12:08

标签: java spring spring-boot spring-security jwt

我正在基于一些开源项目和网上发现的一些文档,开发一个带有Spring引导的JWT实现。

什么有效? 我能够生成我的令牌,当我尝试调用一些安全的方法时,在第一次打击时我被撤销了,这很好。

有什么问题? 一旦我生成令牌,似乎我可以调用我的安全方法而无需添加我的Authorization标头。

我正在调试我的代码并发现我在SecurityContextHolder中设置了身份验证,但是在请求完成后我不会清空此变量。在每个实现中发现没有人这样做,所以我的问题是我是否必须这样做以使我的代码按照假设工作,只有当有一个带有有效令牌的授权头时检索安全路径?

我的代码:

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;

    }
}

这是我的控制器测试类

@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";
    }

}

谢谢

1 个答案:

答案 0 :(得分:0)

所以我的问题的答案需要两个修改:

  1. 我们需要通过将此添加到WebSecurityConfig
  2. 来实现无状态会话管理策略
      

    .sessionManagement()。sessionCreationPolicy(SessionCreationPolicy.STATELESS)

    1. 第二件事是我的代码中的拼写错误,我们必须确保在我们的过滤条件下身份验证为空:
    2.   

      if(username!= null&amp;&amp;   SecurityContextHolder.getContext()。getAuthentication()== null)