用于基于 JWT 的身份验证、验证和授权方案的 Spring Security 过滤器,示例 [英] Spring Security filters for JWT-based authentication, verification and authorization scheme, by example

查看:79
本文介绍了用于基于 JWT 的身份验证、验证和授权方案的 Spring Security 过滤器,示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Java + Spring (and Spring Security) here, interested in implementing a JWT-based auth mechanism for my web service using bearer tokens. My understanding of the proper way of using Spring Security for authentication and authorization is through the use of provided (or custom) filters as follows:

  • 您指定应用中的哪些网址经过身份验证(因此需要经过身份验证的请求才能访问)
    • 这通常在扩展 WebSecurityConfigurerAdapter
    • 的带有 @EnableWebSecurity 注释的网络安全类中完成
    • you specify which URLs in your app are authenticated (and thus require authenticated requests to access)
      • this is typically done in an @EnableWebSecurity-annotated web security class that extends WebSecurityConfigurerAdapter
      • 请求客户端最初应访问此登录端点(身份验证过滤器)以获取可用于进行后续 API 调用的身份验证令牌
      • 此过滤器应接收一种类型的登录请求";包含主体(例如用户名)和凭据(例如密码)的对象
      • 此身份验证过滤器应使用登录请求中包含的主体/凭据来确定它们是否代表系统中的有效用户
        • 如果是这样,则会生成一个身份验证令牌(JWT 等)并以某种方式在响应中将其发送回请求者
        • 否则,如果主体/凭据与系统中的有效用户不匹配,则会返回错误响应并且身份验证失败
        • 如果身份验证令牌有效,请求将继续到授权过滤器(见下文)
        • 否则,如果身份验证令牌无效,验证将失败并且过滤器将错误响应发送回客户端
        • 如果他们这样做了,那么请求被允许继续到任何被写入来处理它的资源/控制器,并且该资源/控制器将响应提供给请求者
        • 如果没有,则会向客户端返回错误响应
        • 理想情况下,这个 authz 过滤器中的逻辑(代码)可以访问添加到资源方法的权限注释,这样我就可以添加端点并为其指定权限,而无需修改 authz 过滤器的代码

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

        So to begin with, if anything I have stated above is a Spring Security (or web security in general) anti-pattern or is misled, please begin by providing course correction and steering me in the right direction!

        假设我或多或少了解身份验证流程"以上正确...

        Assuming I'm more or less understanding the "auth flow" above correctly...

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

        Are there any specific Spring Security filters that take care of all of this for me already, or that can be extended and have a few methods overridden to behave this way? Or anything that comes really close? Looking at the list of authentication-specific Spring Security filters I see:

        • UsernamePasswordAuthenticationFilter ->看起来像是 authn 过滤器的一个不错的候选者,但是在查询字符串中需要 usernamepassword 参数,这对我来说很奇怪,最重要的是,它不会生成 JWT
        • CasAuthenticationFilter ->看起来它用于基于 CAS 的 SSO,不适合在非 SSO 上下文中使用
        • BasicAuthenticationFilter ->用于基于 HTTP 基本身份验证的身份验证,不适用于更复杂的设置
        • UsernamePasswordAuthenticationFilter -> looks like a decent candidate for the authn filter but expects a username and password parameter on the query string which is strange to me, and most importantly, does not generate a JWT
        • CasAuthenticationFilter -> looks like its used for CAS-based SSO and is not appropriate for use in non-SSO contexts
        • BasicAuthenticationFilter -> for HTTP basic authentication-based auth, not appropriate for more sophisticated setups

        至于令牌验证和授权,我(出乎意料地)在 Spring Security 环境中看不到任何符合条件的内容.

        As for token verification and authorization, I (much to my surprise) don't see anything in the Spring Security landscape that could qualify.

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

        Unless anyone knows of JWT-specific filters that I can use or subclass easily, I think I need to implement my own custom filters, in which case I'm wondering how to conigure Spring Security to use them and not use any of these other authentication filters (such as UsernamePasswordAuthenticationFilter) as part of the filter chain.

        推荐答案

        据我所知,您想要:

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

        用户名/密码 ->JWT 本身并不是一种既定的身份验证机制,这就是 Spring Security 还没有直接支持的原因.

        username/password -> JWT isn't an established authentication mechanism on its own, which is why Spring Security doesn't yet have direct support.

        你可以得到它你自己很容易,不过.

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

        First, create a /token endpoint that produces a 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(对于其余部分):

        Second, configure Spring Security to allow HTTP Basic (for the /token endpoint) and JWT (for the rest):

        @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 样板,如果你有兴趣贡献你的努力!

        I think there's appetite to add support for something like this in spring-authorization-server to reduce the /token boilerplate, if you're interested in contributing your efforts!

        这篇关于用于基于 JWT 的身份验证、验证和授权方案的 Spring Security 过滤器,示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆