redis 如何在Spring资源服务器中解码和验证来自Spring授权服务器的定制JWT?

3pvhb19x  于 2023-01-12  发布在  Redis
关注(0)|答案(1)|浏览(320)

我使用的是Spring授权服务器1.0.0,在这里我按照我的要求定制了JWT,如下所示,假设有一个用户“vinay”,他的角色也是“vinay”。
我正在添加额外字段“权限”:JWT中的[{“role”:“ROLE_vinay”}]。下面是JWT的有效负载。
{ "sub": "vinay", "aud": "messaging-client", "nbf": 1671430162, "authority": [ { "role": "ROLE_vinay" } ], "scope": [ "openid" ], "iss": "http://localhost:8000", "exp": 1671430462, "iat": 1671430162 }
角色“vinay”有多个权限/限制。此数据存在于MySQL中。用户验证(登录)后,我将此数据添加到Redis中的KEY:VALUE对中,其中key是“vinay”,value是[限制1,权限1,权限2]。
"vinay" : [Permission 1, Permission 2, Restriction 1]
用户成功登录后(vinay),客户端获得自定义令牌,并将请求发送到spring资源服务器,请求头中带有自定义JWT,并带有Bearer前缀。对于每个请求,如何解码传入的JWT并获得“role”并从Redis验证?如果Redis中有任何限制,它应该不允许。它应该抛出“401 unauthorized”。如果有权限,则应着手处理该请求。

4xrmg8kj

4xrmg8kj1#

在你最新的评论中,你提到只使用MySQL,我认为这是有道理的。我认为Redis不适合这种情况。所以流程是:
1.在授权服务器中,使用OAuth2TokenCustomizer<JwtEncodingContext>authority字段添加到JWT(例如ROLE_xyz)。
1.在资源服务器中,使用JwtGrantedAuthoritiesConverter将JWT的authority字段Map到Spring Security中的authentication.getAuthorities()
挑战在于,在数据库中,您将角色直接Map到一组允许的URL(例如ROLE_xyz =〉POST /customer,...)。也许您正在处理一个现有的数据库模式,无法从头开始设计数据或关系。我所期望的是将角色Map到一组权限(例如ROLE_xyz =〉customer.write,...)。
如果您有这样的权限方案,您可以在上面的JwtGrantedAuthoritiesConverter中进行查找,并将这些权限Map为权限。
但是,如果MySQL中的数据将角色Map到URL模式,则最好使用AuthorizationManager API将授权与MySQL表集成(有关示例,请参见Authorize HttpServletRequests with AuthorizationFilter下的 * Configure RequestMatcherDelegatingAuthorizationManager *)。
可以实现一个定制的AuthorizationManager来查找ROLE_xyz,并使用RequestMatcherDelegatingAuthorizationManager.builder()动态构建授权规则列表。
例如:

@Configuration
@EnableWebMvc
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> access)
            throws Exception {
        // @formatter:off
        http
            .authorizeHttpRequests((authorize) -> authorize
                .anyRequest().access(access)
            )
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
        // @formatter:on

        return http.build();
    }

    @Bean
    AuthorizationManager<RequestAuthorizationContext> authorizationManager(
            HandlerMappingIntrospector introspector) {
        MvcRequestMatcher.Builder mvcMatcherBuilder = new MvcRequestMatcher.Builder(introspector);
        RequestMatcher anyRequest = AnyRequestMatcher.INSTANCE;

        AuthorizationManager<RequestAuthorizationContext> authenticated =
            AuthenticatedAuthorizationManager.authenticated();
        AuthorizationManager<RequestAuthorizationContext> denyAll =
            (a, c) -> new AuthorizationDecision(false);

        return (supplier, context) -> {
            Authentication authentication = supplier.get();
            HttpServletRequest request = context.getRequest();

            // TODO: Look up mappings in MySQL using authentication.getAuthorities()...

            RequestMatcherDelegatingAuthorizationManager.Builder builder =
                RequestMatcherDelegatingAuthorizationManager.builder();

            // TODO: Add custom mappings from MySQL...
            //builder.add(mvcMatcherBuilder.pattern("/customer/**"), authenticated);

            RequestMatcherDelegatingAuthorizationManager delegate =
                builder.add(anyRequest, denyAll).build();

            return delegate.check(() -> authentication, request);
        };
    }

}

这看起来可能很复杂,但是您的授权方案(使用OAuth2 + JWT AND roles/authorities AND MySQL来管理角色)是一个有点复杂的设置,AuthorizationManager API实际上比Spring Security的早期版本更容易。
另请注意,上面的示例假定数据库中的角色Map可以在运行时更改,因此每次都必须进行查找。如果不是这种情况,则可以将示例更改为仅在启动时从MySQL加载数据。如果需要,还可以使用高速缓存来提高MySQL查找性能。

相关问题