No1.搭建基本的密码模式请求token(授权服务端)
首先,暂时不搭建cloud相关的环境,暂时不会使用到数据库和redis等库,用户信息、客户端信息、授权信息等都存储在内存中,只编写跟密码模式相关的代码(此处的密码模式是按照自定义代码编写)仅作为最基础的授权认证服务端。
代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客
目录
C1.AuthorizationServerConfiguration
C2.RegisteredClientConfiguration
D1.OAuth2ResourceOwnerBaseAuthenticationToken
D2.OAuth2ResourceOwnerBaseAuthenticationConverter
D3.OAuth2ResourceOwnerBaseAuthenticationProvider
D4.PigDaoAuthenticationProvider
D5.CustomeOAuth2AccessTokenGenerator
D6.CustomeOAuth2TokenCustomizer
D1.PigAuthenticationFailureEventHandler
D2.PigAuthenticationFailureEventHandler
首先,暂时不搭建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的端点
更多推荐
所有评论(0)