如何在Spring授权服务器的DB中存储Github和Google的Clientid和ClientSecret?

问题描述 投票:0回答:1

我是 Spring 授权服务器的新手,我想使用 Github 登录,所以我首先尝试将 client-id 和 client-secret 放入属性文件中,但在现实世界中它不能存储在属性文件中所以我尝试通过

ClientRegistrationRepository
接口将其存储在数据库中,但在我看来,没有使用“Github”或“Google”字样链接登录来将我重定向到GitHub或Google的授权服务器

之前有没有人尝试过这个,请告诉我有关 Spring 授权服务器的教程或步骤?

  • 问题 在这张图片中我看不到“Github”或“Google”来登录 enter image description here

  • 代码

  • 依赖关系

<?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>3.1.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>authorization-server</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>authorization-server</name>
    <description>authorization-server</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-oauth2-authorization-server</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-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.28</version>
            <scope>provided</scope>
        </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-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

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

</project>

AuthorizationServerConfig.java

package com.example.authorizationserver.config.security;

import com.example.authorizationserver.providers.SuccessfulAuthenticationHandler;
import com.example.authorizationserver.services.ClientRegistrationServiceImpl;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import lombok.AllArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.UUID;

@EnableWebSecurity
@Configuration
@Log4j2
@AllArgsConstructor
public class AuthorizationServerConfig {


    private final SuccessfulAuthenticationHandler successfulLoginWithGoogleOrGithub;
    private final ClientRegistrationServiceImpl clientRegistrationService;

    @Bean
    @Order(1)
    public SecurityFilterChain asFilterChain(HttpSecurity http) throws Exception {

        // we must disable csrf for this endpoint and make it access from any one at the first filter
        http.csrf(csrf -> csrf.ignoringRequestMatchers("/auth/register/**"));

        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);


        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults())
                .oidc(oidc -> oidc.clientRegistrationEndpoint(Customizer.withDefaults()));

        http
                //  Resource server support that allows User Info requests to be authenticated with access tokens
                // this allows me to user /userinfo endpoint in postman with access_token in the header
                .oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()))
                .exceptionHandling(
                c -> c.defaultAuthenticationEntryPointFor(
                        new LoginUrlAuthenticationEntryPoint("/login"),
                        new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                )
        );



        return http.build();
    }

    @Bean
    @Order(2)
    public SecurityFilterChain appFilterChain(HttpSecurity http) throws Exception {


        // we must disable csrf for this endpoint and make it access from any one at the second filter also , because it moved from first filter to
        // the second filter
        http.csrf(csrf -> csrf.ignoringRequestMatchers("/auth/register/**"));


        http.formLogin(Customizer.withDefaults()); // to view to user normal username and password form


        // to display the word : Github to allow user to click on it to log in with username and password of Github
        // and success handler to save user to db after successfully login
        http.oauth2Login(oauth2Login -> oauth2Login
                .clientRegistrationRepository(clientRegistrationService)
                .successHandler(successfulLoginWithGoogleOrGithub));

        return http.build();
    }

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

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Bean
    public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
        return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
    }
    @Bean
    public JWKSource<SecurityContext> jwkSource() {
        KeyPair keyPair = generateRsaKey();
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        RSAKey rsaKey = new RSAKey.Builder(publicKey)
                .privateKey(privateKey)
                .keyID(UUID.randomUUID().toString())
                .build();
        JWKSet jwkSet = new JWKSet(rsaKey);
        return new ImmutableJWKSet<>(jwkSet);
    }

    private static KeyPair generateRsaKey() {
        KeyPair keyPair;
        try {
            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
            keyPairGenerator.initialize(2048);
            keyPair = keyPairGenerator.generateKeyPair();
        }
        catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
        return keyPair;
    }
}

  • OAuth2ClientRegistrationEntity 该实体存储有关 Google 或 GitHub 或任何提供商的凭据,并且我根据名为
    org.springframework.security.oauth2.client.registration.ClientRegistration
  • 的类存储值
package com.example.authorizationserver.entities;


import jakarta.persistence.*;
import lombok.*;

// this entity stored credentials about Google or Github db
@Entity
@Table(name = "oauth2_clients")
@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class OAuth2ClientRegistrationEntity {

    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String registrationId;

    private String clientId;

    private String clientSecret;

    private String clientName;

    private String authenticationMethod;

    private String authorizationGrantType;

    private String redirectUri;

    private String scopes;

    // this store provider details for simplicity
    private String authorizationUri;

    private String tokenUri;

    private String jwkSetUri;

    private String issuerUri;

    private String userInfoUri;

}

  • OAuth2ClientRegistrationRepo 用于从数据库检索 google 或 github 凭据
package com.example.authorizationserver.repositories;

import com.example.authorizationserver.entities.OAuth2ClientRegistrationEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OAuth2ClientRegistrationRepo extends JpaRepository<OAuth2ClientRegistrationEntity,Long> {

    OAuth2ClientRegistrationEntity findByRegistrationId(String registrationId);
}

  • 客户端注册服务Impl
package com.example.authorizationserver.services;


import com.example.authorizationserver.entities.OAuth2ClientRegistrationEntity;
import com.example.authorizationserver.repositories.OAuth2ClientRegistrationRepo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.stereotype.Service;

@Service
@Log4j2
public class ClientRegistrationServiceImpl implements ClientRegistrationRepository {

    @Autowired
    private OAuth2ClientRegistrationRepo oAuth2ClientRegistrationRepo;

    @Override
    public ClientRegistration findByRegistrationId(String registrationId) {
        OAuth2ClientRegistrationEntity entity = oAuth2ClientRegistrationRepo.findByRegistrationId(registrationId);

        if(entity == null) {
            log.error(">>>>> NO REGISTRATION_ID FOUND WITH THIS ID = " + registrationId);
            return null;
        }
        return this.toModel(entity);
    }


    private ClientRegistration toModel(OAuth2ClientRegistrationEntity entity) {
        return ClientRegistration.withRegistrationId(entity.getRegistrationId())
                .clientName(entity.getClientName())
                .clientId(entity.getClientId())
                .clientSecret(entity.getClientSecret())
                .clientAuthenticationMethod(new ClientAuthenticationMethod(entity.getAuthenticationMethod()))
                .authorizationGrantType(new AuthorizationGrantType(entity.getAuthorizationGrantType()))
                .redirectUri(entity.getRedirectUri())
                .scope(
                        entity.getScopes().split(",")
                )
                .authorizationUri(entity.getAuthorizationUri())
                .tokenUri(entity.getTokenUri())
                .jwkSetUri(entity.getJwkSetUri())
                .issuerUri(entity.getIssuerUri())
                .userInfoUri(entity.getUserInfoUri())
                .build();
    }


}

  • OAuth2ClientRegistrationTableInitializer这个类用于将google或github的数据添加到db
package com.example.authorizationserver.config.db;


import com.example.authorizationserver.entities.OAuth2ClientRegistrationEntity;
import com.example.authorizationserver.repositories.OAuth2ClientRegistrationRepo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.core.AuthorizationGrantType;

@Configuration
public class OAuth2ClientRegistrationTableInitializer {

    @Autowired
    private OAuth2ClientRegistrationRepo oAuth2ClientRegistrationRepo;


    public void addAllOAuth2ClientsToDB() {
        OAuth2ClientRegistrationEntity google = OAuth2ClientRegistrationEntity.builder()
                .registrationId("google")
                .clientName("Google")
                .clientId("my-google-client-id")
                .clientSecret("my-google-client-secret")
                .redirectUri("{baseUrl}/login/oauth2/code/{registrationId}")
                .scopes("openid,profile,email,address,phone")
                .authenticationMethod("client_secret_basic")
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
                .authorizationUri("https://accounts.google.com/o/oauth2/v2/auth")
                .tokenUri("https://www.googleapis.com/oauth2/v4/token")
                .userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo")
                .jwkSetUri("https://www.googleapis.com/oauth2/v3/certs")
                .build();

        oAuth2ClientRegistrationRepo.save(google);
    }
}

spring-security spring-oauth2 spring-authorization-server
1个回答
0
投票

为了在生成的登录页面上显示链接,文档状态

为了让

DefaultLoginPageGeneratingFilter
显示已配置的 OAuth 客户端的链接,注册的
ClientRegistrationRepository
还需要实现
Iterable<ClientRegistration>
。参考
InMemoryClientRegistrationRepository

理想情况下,您应该提供自定义登录页面。

© www.soinside.com 2019 - 2024. All rights reserved.