首页 > 解决方案 > 用于基于 JWT 的身份验证、验证和授权方案的 Spring Security 过滤器,例如

问题描述

Java + Spring(和 Spring Security)在这里,有兴趣使用不记名令牌为我的 Web 服务实现基于 JWT 的身份验证机制。我对使用 Spring Security 进行身份验证和授权的正确方法的理解是通过使用提供的(或自定义)过滤器,如下所示:

因此,首先,如果我上面所说的任何内容是 Spring Security(或一般的 Web 安全)反模式或被误导,请首先提供课程纠正并引导我朝着正确的方向前进!

假设我或多或少正确理解了上面的“身份验证流程”......

是否有任何特定的 Spring Security 过滤器已经为我处理了所有这些问题,或者可以扩展并覆盖一些方法以实现这种行为?或者任何非常接近的东西?查看特定于身份验证的 Spring Security 过滤器列表,我看到:

至于令牌验证和授权,我(令我惊讶的是)在 Spring Security 领域看不到任何符合条件的东西。

除非有人知道我可以轻松使用或子类化的特定于 JWT 的过滤器,否则我认为我需要实现自己的自定义过滤器,在这种情况下,我想知道如何配置 Spring Security 以使用它们而不使用任何其他身份验证过滤器(例如UsernamePasswordAuthenticationFilter)作为过滤器链的一部分。

标签: javaspringspring-securityservlet-filters

解决方案


据我了解,您想要:

  1. 通过用户名和密码对用户进行身份验证并使用 JWT 进行响应
  2. 在后续请求中,使用该 JWT 对用户进行身份验证

username/password -> JWT它本身并不是一个既定的身份验证机制,这就是 Spring Security 还没有直接支持的原因。

不过,您可以很容易地自己获得它。

首先,创建一个/token生成 JWT 的端点:

@RestController
public class TokenController {

    @Value("${jwt.private.key}")
    RSAPrivateKey key;

    @PostMapping("/token")
    public String token(Authentication authentication) {
        Instant now = Instant.now();
        long expiry = 36000L;
        // @formatter:off
        String scope = authentication.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.joining(" "));
        JWTClaimsSet claims = new JWTClaimsSet.Builder()
                .issuer("self")
                .issueTime(new Date(now.toEpochMilli()))
                .expirationTime(new Date(now.plusSeconds(expiry).toEpochMilli()))
                .subject(authentication.getName())
                .claim("scope", scope)
                .build();
        // @formatter:on
        JWSHeader header = new JWSHeader.Builder(JWSAlgorithm.RS256).build();
        SignedJWT jwt = new SignedJWT(header, claims);
        return sign(jwt).serialize();
    }

    SignedJWT sign(SignedJWT jwt) {
        try {
            jwt.sign(new RSASSASigner(this.key));
            return jwt;
        }
        catch (Exception ex) {
            throw new IllegalArgumentException(ex);
        }
    }

}

其次,配置 Spring Security 以允许 HTTP Basic(用于/token端点)和 JWT(用于其余部分):

@Configuration
public class RestConfig extends WebSecurityConfigurerAdapter {

    @Value("${jwt.public.key}")
    RSAPublicKey key;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.authorizeRequests((authz) -> authz.anyRequest().authenticated())
            .csrf((csrf) -> csrf.ignoringAntMatchers("/token"))
            .httpBasic(Customizer.withDefaults())
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            .sessionManagement((session) -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .exceptionHandling((exceptions) -> exceptions
                .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                .accessDeniedHandler(new BearerTokenAccessDeniedHandler())
            );
        // @formatter:on
    }

    @Bean
    UserDetailsService users() {
        // @formatter:off
        return new InMemoryUserDetailsManager(
            User.withUsername("user")
                .password("{noop}password")
                .authorities("app")
                .build());
        // @formatter:on
    }

    @Bean
    JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withPublicKey(this.key).build();
    }

}

如果您有兴趣贡献自己的努力,我认为有兴趣添加对此类内容的支持spring-authorization-server以减少样板文件!/token


推荐阅读