Spring Authorization Server 体验

Spring Authorization Server 体验

jdk17 最低要求

介绍

最新版现在支持

  • 授权码模式(authorization_code)
  • 客户端模式(client_credentials)
  • 密码模式(resource owner password credentials)已经被废弃了

引入依赖

创建 spring boot项目,引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-authorization-server</artifactId>
</dependency>

配置AuthorizationServerConfig

@Configuration
public class AuthorizationServerConfig {

    @Bean
    @Order(1)
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

        http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
                .oidc(Customizer.withDefaults());	// Enable OpenID Connect 1.0
        http.exceptionHandling(exceptions -> exceptions.defaultAuthenticationEntryPointFor(
                new LoginUrlAuthenticationEntryPoint("/login"),
                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
        )).oauth2ResourceServer(resourceServer -> resourceServer.jwt(Customizer.withDefaults()));
        return http.build();
    }

    /**
     * 默认发放令牌
     * @return
     */
    @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;
    }

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

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

    @Bean
    public RegisteredClientRepository registeredClientRepository(){
        RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("clientId")
                .clientSecret("{noop}clientSecret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://www.baidu.com")
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .scope("message.read")
                .scope("message.write")
                .scope("all")
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();
        return new InMemoryRegisteredClientRepository(registeredClient);
    }

}

DefaultSecurityConfig

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

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

        http.authorizeHttpRequests((authorize) -> authorize
                .requestMatchers("/oauth2/**")
                                .permitAll()
                        .anyRequest().authenticated()
                )
                .cors(Customizer.withDefaults())
                .csrf((csrf) -> csrf.disable())
                .formLogin(Customizer.withDefaults()) ;

        return http.build();


    }


    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User.withDefaultPasswordEncoder()
                .username("test")
                .password("test")
                .roles("user").build();
        return new InMemoryUserDetailsManager(userDetails);
    }
}

授权token /oauth2/token

GET 请求 http://localhost:9000/oauth2/authorize?response_type=code&client_id=clientId&client_secret=clientSecret&scope=message.read openid&redirect_uri=http://www.baidu.com, 如果有多个 scope ,请用空格隔开

  • response_type:这个意思是相应的方式为code码
  • client_id:即客户端的id
  • scope:请求授权范围
  • redirect_uri:重定向回来的地址

请求后会要求登陆,即我们配置的 UserDetails, 这里是 test/test,登陆后会跳转到授权页面

在这里插入图片描述

所授权的就是我们请求scope 所填的权限,只要同意,就会重定向到我们写的重定向地址,并且拼接了code,即https://www.baidu.com/?code=MwFmihKPXBi

在这里插入图片描述

获取token

使用 POST 方式请求 /oauth2/token, 在填写client_id 时,spring 提供了几种不同的方式

  • client_secret_basic:客户端的 client_id 和 client_secret,在header中增加 Authorization头,Basic +client_id:client_secret
  • client_secret_post:客户端的 client_id 和 client_secret,直接在地址上拼接请求参数如?client_id=xx&client_secret=xxx
  • client_secret_jwt:利用 JWT 进行认证
  • private_key_jwt:方式就是利用 JWT 进行认证。请求方拥有自己的公私钥(密钥对)

我们以第二种方式 client_secret_post 访求,http://localhost:9000/oauth2/token?grant_type=authorization_code&code=ZR4lloWwLsea6f7gs3agbNQ1G3299EVpNJSFL7VgUXwgGFtch75MWKm0M1KOCvnpoapBiyi681ruK52r9ThRPxGaw73TnVdoQn9AY3jCIFlCTOxrtSKF5Q111e5HIHDC&redirect_uri=http://www.baidu.com&client_id=clientId&client_secret=clientSecretcode 就是上一步授权重定向页面所带回来的code :

curl --location --request POST 'http://localhost:9000/oauth2/token?grant_type=authorization_code&code=ZR4lloWwLsea6f7gs3agbNQ1G3299EVpNJSFL7VgUXwgGFtch75MWKm0M1KOCvnpoapBiyi681ruK52r9ThRPxGaw73TnVdoQn9AY3jCIFlCTOxrtSKF5Q111e5HIHDC&redirect_uri=http%3A%2F%2Fwww.baidu.com&client_id=clientId&client_secret=clientSecret' \
--header 'Cookie: JSESSIONID=755740EAE86CD70E8605A263396F21F7'

返回:

{
    "access_token": "eyJraWQiOiIwZTVhMmUzMy1hOT5ZEAYqLTdtLmvTgLTXQ",
    "refresh_token": "9tM8L7Ch56Tsm5cMlDCW9geRoaRei9cJe7Szw9s2n4G8_Y3W7nDp",
    "scope": "openid message.read",
    "id_token": "eyJraWQiOiIwZTVhMmUzMyEZsW7ic5ju67Ag",
    "token_type": "Bearer",
    "expires_in": 299
}

请求资源

http://127.0.0.1:9000/userinfo:

curl --location 'http://127.0.0.1:9000/userinfo' \
--header 'Authorization: Bearer eyJraWQiOiI0ODhiZThlNy1jZWYnFVg' \ # access_token
--header 'Cookie: JSESSIONID=F9C28DB81035EDFC4ADB7B7E50893FB9'

返回:

{
    "sub": "test"
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值