Сводка
Интересно, если я использую webflux и oauth2 без с использованием jwt, я хочу сохранить токен в Redis, как я могу настроить Сервер ресурсов .
Фактическое поведение
Прежде всего, я получил токен, который создается с помощью DefaultTokenServices
и RedisTokenStore
, и когда он был создан, он был сохранен в Redis. Возвращаемое значение было похоже на следующее json:
{
"access_token": "0d0dd242-cbdf-4e47-842b-c6ad5edae332",
"token_type": "bearer",
"refresh_token": "2189ff33-eb96-49d1-ab60-6ad337ae06a9",
"expires_in": 7199,
"scope": "read write"
}
А затем я пытаюсь получить доступ к защищенному ресурсу localhost:3660/api/auth/user
, чтобы получить информацию о текущем пользователе, и добавляю заголовок, такой как Authorization: Bearer 0d0dd242-cbdf-4e47-842b-c6ad5edae332
Тогда я получил 401 Несанкционированный.
Ожидаемое поведение
Я хочу получить доступ к защищенному ресурсу /api/auth/user
, но не могу. Я искал много решений, и до сих пор об этом не задумывался.
Код
Это мой WebSecurityConfig
, я использую модуль Webflux, а не модуль Web.
@Configuration
@EnableWebFluxSecurity
public class WebSecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, ReactiveAuthenticationManager reactiveAuthenticationManager) {
http.authorizeExchange(
exchange -> exchange.pathMatchers(HttpMethod.OPTIONS).permitAll()
.pathMatchers("/api/auth/signup/**", "/api/auth/password").permitAll()
.pathMatchers("/api/auth/login", "/api/auth/admin/login").permitAll()
.pathMatchers(HttpMethod.POST, "/api/auth/setting/mfa").hasAuthority(Role.MFA.getAuthority())
.pathMatchers("/api/auth/setting/sms").hasAnyAuthority(Role.SMS.getAuthority(), Role.USER.getAuthority())
.anyExchange().hasAnyAuthority(Role.USER.getAuthority(), Role.ADMIN.getAuthority(), Role.ROOT.getAuthority())
);
return http
.authenticationManager(reactiveAuthenticationManager)
// .oauth2ResourceServer(oAuth2ResourceServerSpec -> oAuth2ResourceServerSpec.)
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.build();
}
@Bean
@ConditionalOnMissingBean(ReactiveAuthenticationManager.class)
ReactiveAuthenticationManager reactiveAuthenticationManager(DefaultTokenServices tokenServices,
RedisConnection redisConnection) {
return new MscpAuthenticationManager(tokenServices, redisConnection);
}
}
Другая конфигурация:
@Slf4j
@Configuration
@EnableJpaAuditing
@EnableScheduling
public class MscpAuthorizationConfig {
private final RedisConnectionFactory connectionFactory;
private final ClientDetailsService clientDetailsService;
public MscpAuthorizationConfig(RedisConnectionFactory connectionFactory,
ClientDetailsService clientDetailsService) {
this.connectionFactory = connectionFactory;
this.clientDetailsService = clientDetailsService;
}
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(connectionFactory);
}
@Bean
RouterFunction<ServerResponse> routes(AuthenticationHandler authenticationHandler) {
return route()
.path("/api/auth", api -> api
.path("/", authenticationHandler::routes)
)
.build();
}
@Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(tokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
@Bean
public RedisConnection redisConnection() {
return connectionFactory.getConnection();
}
}
AuthenticationHandler:
@Component
@Slf4j
public class AuthenticationHandler {
private final AuthenticationService authenticationService;
public AuthenticationHandler(AuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
public RouterFunction<ServerResponse> routes() {
return route()
.POST("/login", request -> request.bodyToMono(Login.class)
.switchIfEmpty(Mono.error(new ApplicationException(ApiErrors.SYSTEM_ERROR, "request parameters can not be null.")))
.publishOn(Schedulers.elastic())
.doOnNext(System.out::println)
.map(authenticationService::authenticate)
.flatMap(oAuth2AccessToken -> {
String format = String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, oAuth2AccessToken.getValue());
log.info("Token Type is [{}]", oAuth2AccessToken.getTokenType());
log.info("Expired Date is [{}]", oAuth2AccessToken.getExpiration());
return ok()
.header(HttpHeaders.AUTHORIZATION, format)
.header(HttpHeaders.CONTENT_TYPE, "application/json;charset=UTF-8")
.header(HttpHeaders.CACHE_CONTROL, "no-store")
.header(HttpHeaders.PRAGMA, "no-cache")
.bodyValue(oAuth2AccessToken);
})
)
.GET("/user", request -> ok().bodyValue(request.principal()))
.build();
}
}
Это application.yml:
server:
port: 3660
spring:
application:
name: auth-api
redis:
host: 192.168.10.113
lettuce:
pool:
max-wait: 3000ms
max-idle: 15
max-active: 15
min-idle: 3
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
username:
password:
hikari:
maximum-pool-size: 12
minimum-idle: 5
connection-test-query: SELECT 1
url: jdbc:mysql://192.168.10.113:3306/mscp
jpa:
hibernate:
ddl-auto: update
naming:
physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
show-sql: true
database-platform: org.hibernate.dialect.MySQL8Dialect
#jwt:
# alias: mscp
# keystore: mscp.jks
# keypass: bXNjcF9wYXNzd29yZA==
# storepass: bXNjcF9wYXNzd29yZA==
logging:
level:
com.keanu.mscp.auth: debug
org.springframework.web: trace
это gradle:
plugins {
id 'org.springframework.boot' version '2.2.5.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.keanu.mscp'
version = '1.0.0-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
mavenLocal()
}
ext {
set('springCloudVersion', "Hoxton.SR3")
}
dependencies {
implementation 'com.keanu.mscp:java-infra:1.0.0-SNAPSHOT'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.apache.commons:commons-pool2'
// implementation 'org.springframework.cloud:spring-cloud-starter-oauth2'
implementation 'org.springframework.security.oauth:spring-security-oauth2:2.3.8.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-resource-server'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
test {
useJUnitPlatform()
}
Версия
Spring Boot 2.2.5.RELEASE
Spring Cloud Hoxton.SR3
org.springframework.boot: spring-boot-starter -webflux
org.springframework.security.oauth: spring-security-oauth2: 2.3.8.RELEASE
PS
Если что-то я забуду, пожалуйста, сообщите мне ! Спасибо, ребята