【问题标题】:How do I authenticate client application using spring authorization server 1.0.0?如何使用 spring 授权服务器 1.0.0 验证客户端应用程序?
【发布时间】:2022-12-18 19:31:31
【问题描述】:

我正在尝试构建一个 spring boot 客户端应用程序并通过 spring 授权服务器 1.0.0 进行身份验证。但是我无法登录该应用程序。它总是重定向到 spring 授权服务器登录页面。任何人都请帮我提供指导。我在下面描述我的应用程序配置:

授权服务器

port: 9000

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.0</version>
</parent>
<properties>
    <java.version>17</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jdbc</artifactId>
        <version>2.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-authorization-server</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>com.zaxxer</groupId>
        <artifactId>HikariCP</artifactId>
        <version>5.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>31.1-jre</version>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>
    <dependency>
        <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>20041127.091804</version>
    </dependency>
    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>20030211.134440</version>
    </dependency>
</dependencies>  

授权服务器配置

@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {
    private final PasswordEncoder passwordEncoder;

    public AuthorizationServerConfig(PasswordEncoder passwordEncoder) {
        this.passwordEncoder = passwordEncoder;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);
        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());   // Enable OpenID Connect 1.0

        // @formatter:off
        http
                .exceptionHandling(exceptions ->
                        exceptions.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login"))
                )
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt); // Accept access tokens for User Info and/or Client Registration
        // @formatter:on
        return http.build();
    }

    // @formatter:off
    @Bean
    public RegisteredClientRepository registeredClientRepository(JdbcTemplate jdbcTemplate) {

        // Save registered client in db as if in-memory
        JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcTemplate);
        RegisteredClient existingClient = registeredClientRepository.findByClientId("front-client");
        if(existingClient == null){
            RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                    .clientId("front-client")
                    .clientSecret(passwordEncoder.encode("123456"))
                    .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                    .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                    .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                    //.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                    .redirectUri("http://127.0.0.1:8080/login/oauth2/code/front-client-oidc")
                    .redirectUri("http://127.0.0.1:8080/authorized")
                    .scope(OidcScopes.OPENID)
                    .scope("read")                    
                    .tokenSettings(TokenSettings.builder()
                            .accessTokenTimeToLive(Duration.ofDays(30))
                            .refreshTokenTimeToLive(Duration.ofDays(365))
                            .build())
                    //.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                    .build();
            registeredClientRepository.save(registeredClient);
        }
        return registeredClientRepository;
    }
    // @formatter:on

    
    @Bean
    public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
        return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
    }

    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        RSAKey rsaKey = Jwks.generateRsa();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }
}

授权服务器上的安全配置

@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class DefaultSecurityConfig {

    private final CustomAuthenticationProvider customAuthenticationProvider;

    public DefaultSecurityConfig(CustomAuthenticationProvider customAuthenticationProvider) {
        this.customAuthenticationProvider = customAuthenticationProvider;
    }

    // @formatter:off
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http    .csrf().disable()
                .authorizeHttpRequests(authorize ->
                        authorize.anyRequest().authenticated()
                )
                .formLogin(withDefaults()); //for default login page                
        return http.build();
    }
    // @formatter:on

    @Autowired
    public void bindAuthenticationProvider(AuthenticationManagerBuilder authenticationManagerBuilder){
        authenticationManagerBuilder.authenticationProvider(customAuthenticationProvider);
    }
}

客户端应用程序配置

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>  
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity6</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </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.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

属性文件配置

server:
  port: 8080
spring:
  security:
    oauth2:
      client:
        registration:
          front-client-oidc:
            provider: spring
            client-id: front-client
            client-secret: 123456
            authorization-grant-type: authorization_code
            redirect-uri: "http://127.0.0.1:8080/login/oauth2/code/front-client-oidc"
            scope: openid
            client-name: front-client-oidc
          front-client-authorization-code:
            provider: spring
            client-id: front-client
            client-secret: 123456
            authorization-grant-type: authorization_code
            redirect-uri: "http://127.0.0.1:8080/authorized"
            scope: read
            client-name:  front-client-authorization-code
          front-client-client-credentials:
            provider: spring
            client-id: front-client
            client-secret: 123456
            authorization-grant-type: client_credentials
            scope: read,write
            client-name: front-client-client-credentials
        provider:
          spring:
            issuer-uri: http://127.0.0.1:9000

客户端应用程序的安全配置

@EnableWebSecurity
@Configuration(proxyBeanMethods = false)
public class SecurityConfig {

    // @formatter:off
    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.csrf().disable()
            .authorizeHttpRequests(authorize ->
                authorize.anyRequest().authenticated()
            )
            .oauth2Login(oauth2Login ->
                oauth2Login.loginPage("/oauth2/authorization/front-client-oidc"))
            .oauth2Client(withDefaults());
        return http.build();
    }
    // @formatter:on
}

@Controller
public class DefaultController {
    @GetMapping("/")
    public String root() {
        return "redirect:/index";
    }

    @GetMapping("/index")
    public String index() {
        return "index";
    }
}

浏览器控制台

Request URL: http://localhost:8080

redirect to: http://127.0.0.1:9000/login

redirect to: http://127.0.0.1:9000/oauth2/authorize?response_type=code&client_id=front-client&scope=openid&state=SKhb4bxYHH36G9mRVb4Dx4b4MCzLk8J85FiSjnwQjTw=&redirect_uri=http://127.0.0.1:8080/login/oauth2/code/front-client-oidc&nonce=nmGlkPVUvvUFEE3_WhaiKonKcGqQQWUMbP__ABznb6A&continue

redirect to: http://127.0.0.1:8080/login/oauth2/code/front-client-oidc?code=yBikXvgTh3vVDztjLsHeFFzkN7xeskyo9dERmjG002d9Jnjwrw21PpzjzuttTZ4cfp5E4vj9FfqG23jObRFoiiPL3G1IHEIzXa2wf-l8f3iNEGBZ2GTXrSv_6ZxV3biJ&state=SKhb4bxYHH36G9mRVb4Dx4b4MCzLk8J85FiSjnwQjTw%3D

redirect to: http://127.0.0.1:8080/oauth2/authorization/front-client-oidc?error

again redirect to: http://127.0.0.1:9000/login

【问题讨论】:

  • 没有直接关系,但不要禁用 CSRF 保护:您的客户端应用程序正在使用会话

标签: spring-security spring-authorization-server


【解决方案1】:

您应该允许匿名访问loginoauth2(两者都需要用户进行身份验证):

http.authorizeHttpRequests()
    .requestMatchers("/login/**").permitAll()
    .requestMatchers("/oauth2/**").permitAll()
    .anyRequest().authenticated();

【讨论】:

  • 添加但仍然无法正常工作
猜你喜欢
  • 1970-01-01
  • 2013-08-23
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多