oauth2.0 如何在Spring webflux应用程序中使用Keycloak解决基于角色的身份验证

uwopmtnx  于 2023-10-15  发布在  Spring
关注(0)|答案(1)|浏览(111)

我已经创建了Spring Webflux项目,用于保护我使用Keycloak的服务。在Keycloak中,我创建了两个角色“admin”和“user”,以及两个用户,用户Map到“user”角色,管理员Map到“admin”角色。在securityConfig类中,我在config类中设置了一个对admin的请求和一个对使用hasRole(“role”)的用户的请求。
controller.java

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/samp")
public class controller {

    @GetMapping
    public String display() {
        return " role is user";
    }
    @GetMapping("/a")
    public String displayA() {
        return " role is admin";
    }   @GetMapping("/b")
    public String displayaB() {
        return " role is gene";
    }

SecurityConfig.java

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.web.server.SecurityWebFilterChain;

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    
     @Bean
     public SecurityWebFilterChain securityFilterChain(ServerHttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeExchange()
                .pathMatchers("/samp/a").hasRole("admin")
                .pathMatchers("/samp/b").hasRole("user")
                .anyExchange()
                .authenticated()
                .and()
                .exceptionHandling()
                .authenticationEntryPoint(new AuthenticationEntryPoint())
                .accessDeniedHandler(new AccessDeniedHandler());
        http
                .oauth2ResourceServer(oauth->oauth.jwt());

        return http.build();
    }
     
}

AccessDeniedHandler.java

@Component
public class AccessDeniedHandler implements ServerAccessDeniedHandler {

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException denied) {
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);

        Map<String, Object> responseMap = new HashMap<>();
        responseMap.put("status", HttpServletResponse.SC_UNAUTHORIZED);
        responseMap.put("message", "Unauthorized");
        responseMap.put("data", null);

        byte[] responseBytes=null;
        try {
            responseBytes = serializeToJson(responseMap);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return exchange.getResponse().writeWith(Mono.just(exchange.getResponse()
                .bufferFactory().wrap(responseBytes)));
    }
    
    private byte[] serializeToJson(Object obj) throws Exception {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.writeValueAsBytes(obj);
    }
}

application.yml

server:
  port: 8081
    
logging:
  level:
    '[org.springframework.security]': DEBUG
    '[org.keycloak]': DEBUG

 
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: http://localhost:8181/realms/Metaverse
          jwk-set-uri: ${spring.security.oauth2.resourceserver.jwt.issuer-uri}/protocol/openid-connect/certs
          
jwt:
  auth:
    converter:
      resource-id: metaverse_client
      principal-attribute: preferred_username

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.14</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.apigateway</groupId>
    <artifactId>ApiGateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>ApiGateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2021.0.8</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

我已经使用用户凭据生成令牌,但无法访问用户服务获取访问被拒绝错误,如何解决,我错过了什么
我已经通过 Postman 使用客户端ID和令牌URL使用“用户”凭据生成令牌,但无法访问用户服务获得访问拒绝错误,如何解决,我错过了什么
1.在securityconfig class .pathMatchers(“/samp/B”).hasRole(“user”)中
无法访问此使用genenrated以上令牌和相同的事情发生在管理员使用令牌生成的管理员凭据

The output console debug
main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port 8081
2023-09-14 14:01:09.168  INFO 20040 --- [           main] .s.c.n.e.s.EurekaAutoServiceRegistration : Updating port to 8081
2023-09-14 14:01:09.649  INFO 20040 --- [           main] c.i.i.apigateway.ApiServiceApplication   : Started ApiServiceApplication in 5.371 seconds (JVM running for 6.355)
2023-09-14 14:01:38.806  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Disable delta property : false
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Single vip registry refresh property : null
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Force full registry fetch : false
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Application is null : false
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Registered Applications size is zero : true
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Application version is -1: false
2023-09-14 14:01:38.807  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : Getting all instance registry info from the eureka server
2023-09-14 14:01:38.816  INFO 20040 --- [freshExecutor-0] com.netflix.discovery.DiscoveryClient    : The response status is 200
2023-09-14 14:02:21.935 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/logout', method=POST}
2023-09-14 14:02:21.936 DEBUG 20040 --- [     parallel-1] athPatternParserServerWebExchangeMatcher : Request 'GET /samp/b' doesn't match 'POST /logout'
2023-09-14 14:02:21.936 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2023-09-14 14:02:21.938 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/samp/a', method=null}
2023-09-14 14:02:21.938 DEBUG 20040 --- [     parallel-1] athPatternParserServerWebExchangeMatcher : Request 'GET /samp/b' doesn't match 'null /samp/a'
2023-09-14 14:02:21.938 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : No matches found
2023-09-14 14:02:21.938 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : Trying to match using PathMatcherServerWebExchangeMatcher{pattern='/samp/b', method=null}
2023-09-14 14:02:21.940 DEBUG 20040 --- [     parallel-1] athPatternParserServerWebExchangeMatcher : Checking match of request : '/samp/b'; against '/samp/b'
2023-09-14 14:02:21.940 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.u.m.OrServerWebExchangeMatcher : matched
2023-09-14 14:02:21.940 DEBUG 20040 --- [     parallel-1] a.DelegatingReactiveAuthorizationManager : Checking authorization on '/samp/b' using org.springframework.security.authorization.AuthorityReactiveAuthorizationManager@167e4c63
2023-09-14 14:02:21.942 DEBUG 20040 --- [     parallel-1] o.s.s.w.s.a.AuthorizationWebFilter       : Authorization failed: Access Denied
hts6caw3

hts6caw31#

RTFM

您没有使用Converter<Jwt, Collection<GrantedAuthority>> =>配置jwtAuthenticationConverter,访问令牌中的角色(realm_access.rolesresource_access.{client-id}.roles)不会转换为Spring权限。

注意事项

如果要写一个新的应用程序,你最好使用Sping Boot 3.1.3(而不是2.7.14
jwt.auth.converter不是spring Boot 标准属性的一部分,您没有提供@ConfigurationProperties解析它。如果你从教程中复制,你会忘记这一部分(可能还有使用它的身份验证和授权转换器)。
你可能会发现我的OAuth2基础知识和教程很有用.

相关问题