Как реализовать ресурсный сервер без использования jwt - PullRequest
2 голосов
/ 05 апреля 2020

Сводка

Интересно, если я использую 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

Если что-то я забуду, пожалуйста, сообщите мне ! Спасибо, ребята

...