我已经创建了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
1条答案
按热度按时间hts6caw31#
RTFM
您没有使用
Converter<Jwt, Collection<GrantedAuthority>>
=>配置jwtAuthenticationConverter
,访问令牌中的角色(realm_access.roles
和resource_access.{client-id}.roles
)不会转换为Spring权限。注意事项
如果要写一个新的应用程序,你最好使用Sping Boot
3.1.3
(而不是2.7.14
)jwt.auth.converter
不是spring Boot 标准属性的一部分,您没有提供@ConfigurationProperties
解析它。如果你从教程中复制,你会忘记这一部分(可能还有使用它的身份验证和授权转换器)。你可能会发现我的OAuth2基础知识和教程很有用.