在spring boot中扩展keydepot令牌

n3ipq98p  于 2021-07-16  发布在  Java
关注(0)|答案(2)|浏览(308)

我正在使用key斗篷来保护我的spring引导后端。
依赖项:

<dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-boot-2-adapter</artifactId>
            <version>12.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-tomcat7-adapter-dist</artifactId>
            <version>12.0.3</version>
            <type>pom</type>
        </dependency>
        <dependency>
            <groupId>org.keycloak</groupId>
            <artifactId>keycloak-spring-security-adapter</artifactId>
            <version>12.0.3</version>
        </dependency>

安全配置:

@Override
    protected void configure(HttpSecurity http) throws Exception {

        super.configure(http);
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry expressionInterceptUrlRegistry = http.cors()
                .and()
                .csrf().disable()                
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
                .and() 
                .authorizeRequests();

        expressionInterceptUrlRegistry = expressionInterceptUrlRegistry.antMatchers("/iam/accounts/promoters*").hasRole("PROMOTER");
        expressionInterceptUrlRegistry.anyRequest().permitAll();
    }

一切正常!
但是现在我在keydove令牌“roles”中添加了一个新的部分,我需要在spring引导中以某种方式扩展keydove jwt类,并编写一些代码来解析角色信息并将其存储到securitycontext。你能告诉我怎么把目标存档吗?

gk7wooem

gk7wooem1#

我不明白你为什么需要扩展密钥斗篷令牌。keydepot令牌中已经存在角色。我将尝试解释如何访问它,key斗篷有两个角色级别,1)领域级别和2)应用程序(客户端)级别,默认情况下,您的key斗篷适配器使用领域级别,要使用应用程序级别,您需要在application.yml中将propertie key斗篷.use-resource-role-mappings设置为true
如何在realmenter image description中创建角色
如何在这里创建客户图像描述中的角色
角色为admin(realm)和add\u user(application)的用户在此处输入图像描述
要获得访问角色,您可以在keydove适配器中使用keydoveauthenticationtoken类,您可以尝试调用以下方法:

...
public ResponseEntity<Object> getUsers(final KeycloakAuthenticationToken authenticationToken)  {
    final AccessToken token = authenticationToken.getAccount().getKeycloakSecurityContext().getToken();
    final Set<String> roles = token.getRealmAccess().getRoles();
    final Map<String, AccessToken.Access> resourceAccess = token.getResourceAccess();
...
}
...

要使用spring security保护任何路由器,您可以使用此注解,  示例如下:

@PreAuthorize("hasRole('ADMIN')")
@GetMapping("/users")
public ResponseEntity<Object> getUsers(final KeycloakAuthenticationToken token)  {
   return ResponseEntity.ok(service.getUsers());
}

obs:the  使用@preauthorize注解设置key斗篷.use-resource-role-mappings。  如果设置为true,@preauthorize检查token.getrealmaccess().getroles()中的角色,如果设置为false,则检查token.getresourceaccess()中的角色。
如果您想在token中添加任何自定义声明,请让我知道我可以更好地解释。
我在这里介绍了如何设置key斗篷适配器以及  应用程序.yml:
安全配置.java

...
@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Value("${project.cors.allowed-origins}")
    private String origins = "";

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) {
        KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
        keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
        auth.authenticationProvider(keycloakAuthenticationProvider);
    }

    @Bean
    public KeycloakSpringBootConfigResolver keycloakConfigResolver() {
        return new KeycloakSpringBootConfigResolver();
    }

    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(this.authenticationManagerBean());
        filter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy());
        filter.setAuthenticationFailureHandler((request, response, exception) -> {
            response.addHeader("Access-Control-Allow-Origin", origins);
            if (!response.isCommitted()) {
                response.sendError(401, "Unable to authenticate using the Authorization header");
            } else if (200 <= response.getStatus() && response.getStatus() < 300) {
                throw new RuntimeException("Success response was committed while authentication failed!", exception);
            }
        });
        return filter;
    }

    @Override
    protected void configure(final HttpSecurity http) throws Exception {
        super.configure(http);
        http.csrf()
                .disable()
                .authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS, "**").permitAll()
                .antMatchers("/s/**").authenticated()
                .anyRequest().permitAll();

    }
}

应用程序.yml

..
keycloak: 
    enabled: true 
    auth-server-url: http://localhost:8080/auth 
    resource: myclient 
    realm: myrealm 
    bearer-only: true 
    principal-attribute: preferred_username 
    use-resource-role-mappings: true
..
roqulrg3

roqulrg32#

首先,扩展keydrope accesstoken:

@Data
static class CustomKeycloakAccessToken extends AccessToken {

    @JsonProperty("roles")
    protected Set<String> roles;

}

然后:

@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class KeycloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
    @Override
    protected KeycloakAuthenticationProvider keycloakAuthenticationProvider() {
        return new KeycloakAuthenticationProvider() {

            @Override
            public Authentication authenticate(Authentication authentication) throws AuthenticationException {
                KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
                List<GrantedAuthority> grantedAuthorities = new ArrayList<>();

                for (String role : ((CustomKeycloakAccessToken)((KeycloakPrincipal)token.getPrincipal()).getKeycloakSecurityContext().getToken()).getRoles()) {
                    grantedAuthorities.add(new KeycloakRole(role));
                }

                return new KeycloakAuthenticationToken(token.getAccount(), token.isInteractive(), new SimpleAuthorityMapper().mapAuthorities(grantedAuthorities));
            }

        };
    }

    /**
     * Use NullAuthenticatedSessionStrategy for bearer-only tokens. Otherwise, use
     * RegisterSessionAuthenticationStrategy.
     */
    @Bean
    @Override
    protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
        return new NullAuthenticatedSessionStrategy();
    }

    @Override
    protected KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter() throws Exception {
        KeycloakAuthenticationProcessingFilter filter = new KeycloakAuthenticationProcessingFilter(authenticationManagerBean());
        filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy());
        filter.setRequestAuthenticatorFactory(new SpringSecurityRequestAuthenticatorFactory() {

            @Override
            public RequestAuthenticator createRequestAuthenticator(HttpFacade facade,
                                                                   HttpServletRequest request, KeycloakDeployment deployment, AdapterTokenStore tokenStore, int sslRedirectPort) {
                return new SpringSecurityRequestAuthenticator(facade, request, deployment, tokenStore, sslRedirectPort) {

                    @Override
                    protected BearerTokenRequestAuthenticator createBearerTokenAuthenticator() {
                        return new BearerTokenRequestAuthenticator(deployment) {

                            @Override
                            protected AuthOutcome authenticateToken(HttpFacade exchange, String tokenString) {
                                log.debug("Verifying access_token");
                                if (log.isTraceEnabled()) {
                                    try {
                                        JWSInput jwsInput = new JWSInput(tokenString);
                                        String wireString = jwsInput.getWireString();
                                        log.tracef("\taccess_token: %s", wireString.substring(0, wireString.lastIndexOf(".")) + ".signature");
                                    } catch (JWSInputException e) {
                                        log.errorf(e, "Failed to parse access_token: %s", tokenString);
                                    }
                                }
                                try {
                                    TokenVerifier<CustomKeycloakAccessToken> tokenVerifier = AdapterTokenVerifier.createVerifier(tokenString, deployment, true, CustomKeycloakAccessToken.class);

                                    // Verify audience of bearer-token
                                    if (deployment.isVerifyTokenAudience()) {
                                        tokenVerifier.audience(deployment.getResourceName());
                                    }
                                    token = tokenVerifier.verify().getToken();
                                } catch (VerificationException e) {
                                    log.debug("Failed to verify token");
                                    challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.INVALID_TOKEN, "invalid_token", e.getMessage());
                                    return AuthOutcome.FAILED;
                                }
                                if (token.getIssuedAt() < deployment.getNotBefore()) {
                                    log.debug("Stale token");
                                    challenge = challengeResponse(exchange,  OIDCAuthenticationError.Reason.STALE_TOKEN, "invalid_token", "Stale token");
                                    return AuthOutcome.FAILED;
                                }
                                boolean verifyCaller;
                                if (deployment.isUseResourceRoleMappings()) {
                                    verifyCaller = token.isVerifyCaller(deployment.getResourceName());
                                } else {
                                    verifyCaller = token.isVerifyCaller();
                                }
                                surrogate = null;
                                if (verifyCaller) {
                                    if (token.getTrustedCertificates() == null || token.getTrustedCertificates().isEmpty()) {
                                        log.warn("No trusted certificates in token");
                                        challenge = clientCertChallenge();
                                        return AuthOutcome.FAILED;
                                    }

                                    // for now, we just make sure Undertow did two-way SSL
                                    // assume JBoss Web verifies the client cert
                                    X509Certificate[] chain = new X509Certificate[0];
                                    try {
                                        chain = exchange.getCertificateChain();
                                    } catch (Exception ignore) {

                                    }
                                    if (chain == null || chain.length == 0) {
                                        log.warn("No certificates provided by undertow to verify the caller");
                                        challenge = clientCertChallenge();
                                        return AuthOutcome.FAILED;
                                    }
                                    surrogate = chain[0].getSubjectDN().getName();
                                }
                                log.debug("successful authorized");
                                return AuthOutcome.AUTHENTICATED;
                            }

                        };
                    }
                };
            }
        });
        return filter;
    }

}

相关问题