拥抱Spring全新OAuth解决方案(一)

前言
新旧版本对比
总结


前言


spring security 5.2.x以后,Spring Security Oauth2 不再维护,取而代之的是新的组件Spring Authorization Server
拥抱Spring全新OAuth解决方案(一)

最近ChatGPT异常火爆,看看它能不能帮我写完这个公众号。

拥抱Spring全新OAuth解决方案(一)

看来暂时还不行,那只能亲自编辑了。

首先只需要引入

<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-authorization-server</artifactId>
</dependency>
内部已经引入了其他核心包
 <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-config</artifactId>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-web</artifactId>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-core</artifactId>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-jose</artifactId>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-oauth2-resource-server</artifactId>
      <scope>compile</scope>
    </dependency>

本文以开源项目PIG 基于新版的认证体系来讲解。大概感兴趣可以体验一下。

拥抱Spring全新OAuth解决方案(一)


1.新旧版本对比


     新版组件Spring Authorization Server 不再提供显示tokenEndpoint类,用户可以自己去定义自己的登录端点。

1.1 tokenEndpoint对比

我们先看老板本的tokenEndPoint提供了哪些方法

拥抱Spring全新OAuth解决方案(一)

处理获取token的方法外,其他都是异常的处理。

    我们再看看新版的,新版没有tokenEndPoint了,换成了OAuth2TokenEndpointConfigurer,成了一个名副其实的配置类。

拥抱Spring全新OAuth解决方案(一)

将token相关的个性化配置全部收集在了一起。比如token转化accessTokenRequestConverter,认证管理器AuthenticationProvider,请求匹配器RequestMatcher等等。

对比我们在Spring Security Oauth2中更加清晰明了。在Spring Security Oauth2中, 获取token整个流程为tokenEndpoint–>tokenGranter–>AuthenticationManager–>ProviderManager–>Tokenservice –>AccessTokenConverter–>token。
你只有对源码非常清楚的情况下才知道哪些可以扩展。而且每个核心类之间彼此没有很强的关联。

1.2 tokenEndpoint token端口相关配置

在老版Spring Security Oauth2中,tokenEndpoint提供了默认登录端口:/oauth/token

拥抱Spring全新OAuth解决方案(一)

Spring Authorization Server中,默认提供了如下端口的配置

public final class AuthorizationServerSettings extends AbstractSettings {

    public static Builder builder() {
    
        return new Builder()
                .authorizationEndpoint("/oauth2/authorize")
                .tokenEndpoint("/oauth2/token")
                .jwkSetEndpoint("/oauth2/jwks")
                .tokenRevocationEndpoint("/oauth2/revoke")
                .tokenIntrospectionEndpoint("/oauth2/introspect")
                .oidcClientRegistrationEndpoint("/connect/register")
                .oidcUserInfoEndpoint("/userinfo");
    }   
如获取token的端口 /oauth2/token,token自省端口/oauth2/introspect
public final class AuthorizationServerSettings extends AbstractSettings {

    public static Builder builder() {
    
        return new Builder()
                .authorizationEndpoint("/oauth2/authorize")
                .tokenEndpoint("/oauth2/token")
                .jwkSetEndpoint("/oauth2/jwks")
                .tokenRevocationEndpoint("/oauth2/revoke")
                .tokenIntrospectionEndpoint("/oauth2/introspect")
                .oidcClientRegistrationEndpoint("/connect/register")
                .oidcUserInfoEndpoint("/userinfo");
    }   

以上这些端口,组件都无需定义具体的api.

当然如果你想修改这些端口,只需自定义即可。如改成/oauth2/token/test

@Bean 
public AuthorizationServerSettings authorizationServerSettings() {
    return AuthorizationServerSettings
    .builder()
    .tokenEndpoint("/oauth2/token/test")
    .build();
}

获取token的请求便是

拥抱Spring全新OAuth解决方案(一)


1.3. Oauth2协议其他配置对比

1.3.1 资源服务器配置

先看老版本SpringSecurityOauth2,需要继承ResourceServerConfigurerAdapter
@Configuration
@EnableResourceServer
protected class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf()
                .disable()
                // 开启跨域
                .cors().and()
                .exceptionHandling()
                .authenticationEntryPoint(customerAuthenticationEntryPoint())
                .accessDeniedHandler(customerOAuth2AccessDeniedHandler())
                .and()
                .authorizeRequests()
                .antMatchers(
                        "/oauth/**",
                        ...
                       )
                .permitAll()
                .anyRequest()
                .authenticated();
    }


新版本只需配置过滤器链SecurityFilterChain(PIG 项目配置,和官网差不多)

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http
                // 开放自定义的部分端点
                .authorizeRequests(authorizeRequests -> authorizeRequests.antMatchers("/token/*").permitAll()
                .anyRequest().authenticated())
                .headers()
                .frameOptions()
                .sameOrigin()// 避免iframe同源无法登录
                .and()
                 // 表单登录个性化
                .apply(new FormIdentityLoginConfigurer());
        // 处理 UsernamePasswordAuthenticationToken
        http.authenticationProvider(new PigDaoAuthenticationProvider());
        return http.build();
    }

public final class FormIdentityLoginConfigurer
        extends AbstractHttpConfigurer
<FormIdentityLoginConfigurer, HttpSecurity> {

    @Override
    public void init(HttpSecurity http) throws Exception {
        http.formLogin(formLogin -> {
            formLogin.loginPage("/token/login");
            formLogin.loginProcessingUrl("/token/form");
            formLogin.failureHandler(new FormAuthenticationFailureHandler());

        }).logout() // SSO登出成功处理
                .logoutSuccessHandler(new SsoLogoutSuccessHandler()).deleteCookies("JSESSIONID")
                .invalidateHttpSession(true).and().csrf().disable();
    }

}                .logoutSuccessHandler(new SsoLogoutSuccessHandler()).deleteCookies("JSESSIONID")
                .invalidateHttpSession(true).and().csrf().disable();
    }

}

1.3.2 认证服务器配置

在老版本SpringSecurityOauth2,需要继承AuthorizationServerConfigurerAdapter

@Configuration
@EnableAuthorizationServer

protected class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints
                //token存储
                .tokenStore(tokenStore())
                //异常翻译器
                .exceptionTranslator(webResponseExceptionTranslator)
                //token转化器
                .accessTokenConverter(accessTokenConverter())
                //自省处理器
                .userDetailsService(customerUserDetailService)
                //认证管理器
                .authenticationManager(authenticationManager);
    }


新版本同样只需配置过滤器链SecurityFilterChain(同样以PIG 项目配置,和官网差不多)

@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
    OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
    // 个性化认证授权端点
    http.apply(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {
        // 注入自定义的授权认证Converter
        tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter())
                // 登录成功处理器
                .accessTokenResponseHandler(new PigAuthenticationSuccessEventHandler())
                // 登录失败处理器
                .errorResponseHandler(new PigAuthenticationFailureEventHandler());
        // 个性化客户端认证        
    }).clientAuthentication(oAuth2ClientAuthenticationConfigurer ->
    // 处理客户端认证异常
    oAuth2ClientAuthenticationConfigurer.errorResponseHandler(new PigAuthenticationFailureEventHandler()))
            // 授权码端点个性化confirm页面
            .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint
                    .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)));

    RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher();
    DefaultSecurityFilterChain securityFilterChain = http.requestMatcher(endpointsMatcher)
            .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated())
            // redis存储token的实现
            .apply(authorizationServerConfigurer.authorizationService(authorizationService)
                    //token端口配置
                    .authorizationServerSettings(AuthorizationServerSettings.builder()
                            .issuer(SecurityConstants.PROJECT_LICENSE).build()))
            // 授权码登录的登录页个性化
            .and().apply(new FormIdentityLoginConfigurer()).and().build();

    // 注入自定义授权模式实现
    addCustomOAuth2GrantAuthenticationProvider(http);
    return securityFilterChain;
}
表面上新版的所需要配置的类多了一些,事实上它是将所有可以需要配置的类都聚合到了一起。更加清晰灵活。后面再单独分析上次的个性化配置

1.4.许可类型对比

1.4.1 密码模式默认不支持


Spring Authorization Server 是基于OAuth 2.1

目前支持的许可类型有(https://docs.spring.io/spring-authorization-server/docs/current/reference/html/overview.html)

拥抱Spring全新OAuth解决方案(一)

可以看到默认不支持密码模式,当然它支持用户自定义许可类型。如pig项目自定义密码许可模式的扩展(下一章分析)

1.4.2 新增Opaque Token

Opaque Token是OAuth2.1中定义的一种访问令牌类型,与JWT(JSON Web Token)不同,它是一种无法解析的令牌。具体来说,Opaque Token是由授权服务器在颁发访问令牌时,生成的加密字符串,仅包含哈希值和元数据信息,而不包含具体的用户信息和权限信息
Opaque Token的特点是令牌本身无法被解析和读取,仅能通过授权服务器提供的Token Introspection Endpoint接口来解析并验证该令牌的有效性和访问权限,因此相对于JWT,Opaque Token更加安全可靠。
Opaque Token一般用于对API服务进行访问控制和鉴权,授权服务器可以对令牌进行透明的刷新、吊销或延期处理,也方便了服务端对令牌进行管理和维护。

我们看官网给的介绍,Opaque Token是怎样工作的。https://docs.spring.io/spring-security/reference/servlet/oauth2/resource-server/opaque-token.html

拥抱Spring全新OAuth解决方案(一)

也是就说暴露给外部的是一个ID token,即Opaque Token,我们需要通过Introspect来获取jwt等用户认证信息

2.总结


    正如chatGPT所言,Spring Authorization Server 相比Spring Security Oauth2更加简单和标准化。整个授权过程也清晰很多。

拥抱Spring全新OAuth解决方案(一)

下一篇将从源码层面分析pig项目基于Spring Authorization Server的

扩展和实现。


拥抱Spring全新OAuth解决方案(一)
关注我的你,是最香哒!


原文始发于微信公众号(小李的源码图):拥抱Spring全新OAuth解决方案(一)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/145289.html

(0)
青莲明月的头像青莲明月

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!