代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客

目录

A1.框架搭建

A2.代码实现

B1.配置类

C1.AuthorizationServerConfiguration

C2.RegisteredClientConfiguration

C3.WebSecurityConfiguration

B2.认证授权核心类

C1.业务类

        D1.OAuth2ResourceOwnerBaseAuthenticationToken

        D2.OAuth2ResourceOwnerBaseAuthenticationConverter

        D3.OAuth2ResourceOwnerBaseAuthenticationProvider

        D4.PigDaoAuthenticationProvider

        D5.CustomeOAuth2AccessTokenGenerator

        D6.CustomeOAuth2TokenCustomizer

        D7.OAuth2AccessTokenClaims

C2.处理类

        D1.PigAuthenticationFailureEventHandler

        D2.PigAuthenticationFailureEventHandler

B3.端点类

C1.PigTokenEndpoint


首先,暂时不搭建cloud相关的环境,暂时不会使用到数据库和redis等库,用户信息、客户端信息、授权信息等都存储在内存中,只编写跟密码模式相关的代码(此处的密码模式是按照自定义代码编写)

仅作为最基础的授权认证服务端。

A1.框架搭建

按照pig项目框架搭建即可,当前需要的都有是:

“#”符号表示重点的包名

com.pig4cloud.pig  
​
├── pig-auth            // 认证授权核心模块
│       └── # config                        // 认证授权和安全配置
│       └── # endpoint                      // 认证授权端点
│       └── # support                       // 认证授权核心类
├── pig-common         // 框架核心模块
│       └── pig-common-bom                      // 定义common全局jar版本模块
│       └── pig-common-common                   // 公共工具类核心模块
│           └── # constant                      // 公共常量包
│           └── # util                          // 公共工具包
│       └── pig-common-security                 // 安全工具模块
│           └── # util                          // 安全工具包

现在主要就是pig-auth包实现核心代码

重点:pig使用的是undertow容器,已在spring-boot-starter-web中排除tomcat

A2.代码实现

最重要会涉及到两个过滤器:OAuth2ClientAuthenticationFilter(进行客户端认证)、OAuth2TokenEndpointFilter(进行生成accesstoken认证)

整体逻辑是,先写配置类,想好都需要哪些自定义类,先将配置文件补充完整,然后在去补充自定义代码逻辑。

配置类首先分为两大部分:授权配置、安全配置;
授权配置包含:授权过滤链逻辑,其中就夹杂着端点配置,token生成配置,先将自定义类写好(逻辑可以先空着);
安全配置包括:安全过滤链逻辑(也可以不加,就看需不需要新的端点,或者是否自定义端点了),用户信息的生成;
端点类主要就是除授权断点外的其他逻辑,例如自定义的校验token或者token查询等端点,也可以不加;
认证授权核心类,分为两大类:认证授权业务类和最终结果处理类;
业务类,就包含常用的自定义的converter、provider、token类,和tokenGenerator等类;
处理类,就是handler,successHandler、failureHandler等;

B1.配置类

C1.AuthorizationServerConfiguration

主要配置授权过滤器,其中会涉及到一些自定义的类:

@RequiredArgsConstructor
@Configuration
public class AuthorizationServerConfiguration {
​
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SecurityFilterChain authenticationServerSecurityFilterChain(HttpSecurity http) throws Exception {
        //new一个 OAuth2授权服务的配置
        //配置个性化客户端认证
            //配置授权失败的处理器
        //配置个性化认证授权端点(获取accestoken端点)
            // 注入个性化的授权认证Converter
            //配置认证失败的处理器
            //配置认证成功的处理器
        //配置过滤链拦截的端点(过滤链默认是任意端点,可以通过这个设置,只有匹配中这写端点,才会进入这个过滤链)
        //配置端点的权限(默认提供的oauth2的端点是需要认证权限的,例如/oauth2/introspect)
        //配置端点元数据统一发行路径,其中各个端点路径都有默认使用值(也可以自定义配置,例如使用.authorizationEndpoint()配置授权请求端点)
        //忽略掉相关断点的 csrf
        //build SecurityFilterChain
        //注入自定义授权模式认证实现类
        //返回
​
        return securityFilterChain;
    }
​
    public AuthenticationConverter accessTokenRequestConverter(){
        //new一个token转换器委托器,其中包含自定义密码模式认证转换器和刷新令牌认证转换器
        return;
    }
​
    public void addCustomOAuth2GrantAuthenticationProvider(HttpSecurity http){
        //从shareObject中获取到授权管理业务类(主要负责管理已认证的授权信息)
        //从shareObject中获取到认证管理类
        //new一个自定义处理密码模式的授权提供方,其中重点需要注入token生成器
        //new一个自定义用户认证提供方,(类似于DaoAuthenticationProvider)需要注入用户管理业务userDetailsService,当前用的是InMemoryUserDetailsManager,生产模式最好不要使用
        // 将自定义处理密码模式的授权提供方添加到安全配置中
        // 将自定义用户认证提供方添加到安全配置中
        
    }
​
    @Bean
    public OAuth2TokenGenerator oAuth2TokenGenerator(){
        //new一个token生成器委托器,其中包含自定义accesstoken生成器和refreshtoken生成器
        return ;
    }
​
    @Bean
    UserDetailsService userDetailsService(){
        //new一个用户管理业务,注入一盒用户信息
        return ;
    }
​
}

C2.RegisteredClientConfiguration

主要是客户端信息的持久化管理、认证信息的持久化管理:

@RequiredArgsConstructor
@Configuration
public class RegisteredClientConfiguration {
​
    @Bean
    public OAuth2AuthorizationService oAuth2AuthorizationService(){
        //new一个授权管理业务
        return oAuth2AuthorizationService;
    }
​
    /**
     * @Description: 注册一个客户端应用
     *
     * @return the registered client repository
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        //new一个客户端管理业务,这里用的基于内存持久的,一定要注入一个客户端信息,否则无法使用
        return inMemoryRegisteredClientRepository;
    }
​
​
    /**
     * @Description: 创建客户端信息
     * @param id
     * @return
     */
    private RegisteredClient createRegisteredClient(final String id) {
        return ;
    }
​
}

C3.WebSecurityConfiguration

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration {
​
    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        //根据业务开放自定义的部分端点
        //其余端点都需要登录
        //配置自定义用户认证提供方
​
        return http.build();
    }
​
}

B2.认证授权核心类

C1.业务类

D1.OAuth2ResourceOwnerBaseAuthenticationToken

token类是最简单的,主要就是继承AbstractAuthenticationToken生成token,最主要的就是构造函数。

由于这些认证参数在不同的授权模式下可以复用,所以把它抽取成抽象类,特殊情况再特殊处理。

继承类OAuth2ResourceOwnerPasswordAuthenticationToken

D2.OAuth2ResourceOwnerBaseAuthenticationConverter

converter类也不是特别麻烦,继承OAuth2ResourceOwnerBaseAuthenticationConverter ,主要是将request的params、body入参转化一下,并和principal一起拿到token,这里只需要检验入参格式等信息。

由于这个过程可以复用,所以把它抽取成抽象类,特殊情况再特殊处理。

@Override
    public Authentication convert(HttpServletRequest request) {
        //校验是否支持此convert,根据grantType校验
        //校验参数,个性化的看具体需求
        // 获取当前已经认证的客户端信息,这里的数据,来自于 OAuth2ClientAuthenticationFilter 过滤器创建的
        //获取请求的scopes信息
        //获取附加信息
        //new一个token,根据已认证客户端、请求scopes、和附加信息
        return null;
    }
​

继承类OAuth2ResourceOwnerPasswordAuthenticationConverter

D3.OAuth2ResourceOwnerBaseAuthenticationProvider

重点就是AccessToken授权provider类,这个类是拿到最终AccessToken授权信息的。首先会有针对客户端认证信息的判断,然后从converter里面拿到信息去进行用户认证,这里用的用户认证provider是自定义的,拿到用户信息后,就可以生成AccessToken了,最后将AccessToken授权信息进行持久化,然后最终返回AccessTokenAuthenticationToken。

由于这个过程可以复用,所以把它抽取成抽象类,特殊情况再特殊处理(类似于AbstractUserDetailsAuthenticationProvider类的继承逻辑一样)

//AccessToken授权provider类最重要的方法:#authenticate()
​
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
​
        //校验客户端principal是否已有效认证
        //检验认证范围是否 supports AuthorizationGrantType.PASSWORD = password
        //校验请求的scopes是否全部包含在授权客户端信息中
        //根据附加信息从manager中认证校验用户信息
        //先搭建 OAuth2Authorization.Builder,在后面会set值(放这里是因为可以在不同的 token 里面进行赋值操作)
​
        //---- 构建Access token ----
        //1.搭建 OAuth2TokenContext.build
        //2.搭建 OAuth2TokenContext
        //3.生成 token,判断是否为空
        //4.new OAuth2AccessToken
        //5.赋值到 OAuth2Authorization.Builder
​
        //---- 构建 Refresh token ----
        //1.判断是否需要生成 Refresh token
        //2.需要就判断是否有默认的 refreshTokenGenerator
        //3.有默认生成器就用默认的
        //4.没有默认的,就修改 OAuth2TokenContext.build 的值,并搭建 OAuth2TokenContext
        //5.生成 token,判断是否为空
        //6.赋值到 OAuth2Authorization.Builder
​
        //搭建 OAuth2Authorization,并存储客户端已认证信息,进行持久化
​
         //new 并返回 OAuth2AccessTokenAuthenticationToken
        return null;
    }
​

继承类OAuth2ResourceOwnerPasswordAuthenticationProvider;

有三个方法需要重写:

/**
     * @Description: 当前provider是否支持此令牌类型,由子类重写
     * @param authentication
     * @Return: boolean
     */
    @Override
    public abstract boolean supports(Class<?> authentication);
​
    /**
     * @Description: 当前的请求客户端是否支持此模式,由子类重写
     * @param registeredClient
     * @Return: void
     */
    public abstract void checkClient(RegisteredClient registeredClient);
​
    /**
     * @Description: 用户认证前统一构建 UsernamePasswordAuthenticationToken 格式,由子类重写
     * @param reqParameters
     * @Return: org.springframework.security.authentication.UsernamePasswordAuthenticationToken
     */
    public abstract UsernamePasswordAuthenticationToken buildToken(Map<String, Object> reqParameters);

D4.PigDaoAuthenticationProvider

其次就是用户认证PigDaoAuthenticationProvider,就是获取存储的用户信息,能拿到就对比密码,一致就返回已认证的token。这里就继承已封装好的AbstractUserDetailsAuthenticationProvider,#authenticate()方法就是用父类的,我们主要重写 #retrieveUser() #additionalAuthenticationChecks()两个方法,其余的根据业务需要进行重写:

   @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        //判断是否有credentials,没有就抛异常
        //有就进行对比,失败就抛异常
    }
​
    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        //准备攻击保护信息(准备不存在时的对比密码)
        //根据 username 获取用户信息
        //若没获取到则抛出UsernameNotFoundException
        //获取到则返回
        //捕获到 UsernameNotFoundException 时,要在进行一次无意义的密码比较(缓解定时攻击),(如果用户不存在也会进行密码对比,防止由于时间信息、功率消耗等信息推敲出用户不存在)
​
        return null;
    }

D5.CustomeOAuth2AccessTokenGenerator

在OAuth2ResourceOwnerBaseAuthenticationProvider中我们有生成token,这里的token是自定义的,所以需要提供一个AccessTokenGenerator:

    @Override
    public OAuth2Token generate(OAuth2TokenContext oAuth2TokenContext) {
        //校验 OAuth2TokenType 和 OAuth2TokenFormat
        //搭建 OAuth2TokenClaimsSet.Builder
        //校验 context 是否有 issuer
        //校验 context 是否有 授权scopes
        //添加token相关信息
        //判断是否有增强定制器 Customizer
        //有则搭建 OAuth2TokenClaimsContext.Builder ,并配置相关信息
        //执行增强器
        //构建 OAuth2TokenClaimsSet
        //组装 access_token ,token格式 = client:username:uuid
​
        //new OAuth2AccessTokenClaims,返回自定义类型的,可以添加claims
        return null;
    }

D6.CustomeOAuth2TokenCustomizer

由于某些业务操作,需要认证时携带用户信息返回,所以我们可以实现OAuth2TokenCustomizer进行增强,implements OAuth2TokenCustomizer

    @Override
    public void customize(OAuth2TokenClaimsContext oAuth2TokenClaimsContext) {
        //拿到生成器里的 OAuth2TokenClaimsSet.Builder
        //向 Builder 里面 claim(k,v) 进去信息, claims 是 map 类型
        //key = SecurityConstants.DETAILS_LICENSE
        //key = SecurityConstants.CLIENT_ID
        //key = SecurityConstants.DETAILS_USER
        //这里不需要返回哦,因为这里的操作的 OAuth2TokenClaimsSet.Builder 就是生成器里面 build 的,这里直接进行的插入操作
    }

D7.OAuth2AccessTokenClaims

由于增强了 accesstoken ,我们就需要有个能承载 claims 的 token,所以创建一个自定义内部类实现OAuth2AccessToken,extends OAuth2AccessToken

        private final Map<String, Object> claims;
​
        public OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt, Map<String, Object> claims) {
            super(tokenType, tokenValue, issuedAt, expiresAt);
            this.claims = claims;
        }
​
        @Override
        public Map<String, Object> getClaims() {
            return this.claims;
        }

C2.处理类

D1.PigAuthenticationFailureEventHandler

实现 AuthenticationSuccessHandler 接口,从filter中拿到HttpServletRequest , HttpServletResponse , Authentication ,然后封装输出;

D2.PigAuthenticationFailureEventHandler

实现 AuthenticationFailureHandler 接口,从filter中拿到HttpServletRequest , HttpServletResponse , AuthenticationException ,然后封装输出;

B3.端点类

C1.PigTokenEndpoint

添加了一个"/check_token"校验token的端点

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐