Spring 基于 JWT 的身份验证、验证和授权方案的安全过滤器,举例

Spring Security filters for JWT-based authentication, verification and authorization scheme, by example

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

所以首先,如果我上面所说的任何内容是 Spring 安全(或一般的网络安全)反模式或被误导,请首先提供路线更正并引导我正确方向!

假设我或多或少地正确理解了上面的“授权流程”...

是否有任何特定的 Spring 安全过滤器已经为我处理了 所有 的问题,或者可以扩展并重写一些方法来执行这边走?或者任何非常接近的东西?查看特定于身份验证的 Spring 安全过滤器列表,我看到:

至于令牌验证和授权,我(令我惊讶的是)在 Spring 安全环境中没有看到 任何东西

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

据我了解,您想:

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

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

不过你可以得到它 on your own pretty easily

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

@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 安全性以允许 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 样板文件,如果您有兴趣贡献自己的力量!