解密Spring Security:多用户类型认证授权、刷新Token,助您构建安全完备的应用

前言

对于spring secutiry 来讲,其默认是只支持一种数据类型进行认证的,因为最后组件的方法仅为:loadUserByUsername(String username) 方法。

如果有多个系统来使用这个认证模块,会导致查询问题。这也就是不支持多用户类型认证的原因。

因为是多个系统,所以不同系统需要到自己的系统或者表中查询自己的数据,而单单一个参数username显然是不能满足需求的,这里至少还需一个参数来区别不同系统才行。

尽管对于不同系统可以通过增加自定义类型的方法来清醒请求access_token,即调用接口

oauth/token 来获取认证信息。

即如下:

aebea7285e224634f30bec7f23b93d1c.jpeg

但是,在使用 刷新token接口的时候,根据spring security 的机制和上面所说,还是会调用默认的loadUserByUsername 方法,导致程序报错。

这里就需要从程序的角度进行修改。这就是这里介绍的多用户数据类型的情况。

即都是刷新接口,都是调用/oauth/token接口,对应grant_type类型都是password_code类型,

对应在接口中增加一个userType即可。


刚才讲到,自定义授权类型的情况,对于兼容其他系统认证的情况,个人感觉还是不合适的,也就是为了兼容其他系统登录认证最合适的做法是稍后下面的做法,而不是增加授权类型这种这种的方式。

因为,即使获取access_token通过增加授权类型解决了,同样,刷新token还是需要更改,而2者内部其实是使用的同一套东西。

整个过程核心机制是通过接口中增加一个参数如userType来进行识别不同的系统类型,

后台逻辑中通过这个类型而进行判断。

1 登陆获取token

1.1 新增ZltUserDetailsService extends UserDetailsService,

新增自定义的方法。

因直接使用项目中内容,类方法就没有改,自定义适配的可以叫做:

CustomUserDetailsService

36a177a70039c46948713bdc84b7e2e2.jpeg

具体的实现逻辑,后续即可根据userType的值进行不同的执行逻辑:

6a9b7c9ebd1ccc27c5ce03a85eb25bc4.jpeg

需要注意:这里的userType 是接口传过来的。

后面的所有需要用到userDetailsService的,全部都要替换成自定义ZltUserDetailsService

1.2 复制org.springframework.security.authentication.dao.DaoAuthenticationProvider的代码,自定义 CustomAuthenticationProvider(注意写法,是实现AbstractUserDetailsAuthenticationProvider并将DaoAuthenticationProvider内容复制到此类里面),然后进行修改retrieveUser()方法,其他不需要动.

如下:

eb422f6b56b71fbb46024ce06ba31bfa.jpeg

1.修改 retrieveUser 方法:

可以根据实际逻辑进行编码即可,这里是调用上面逻辑中自定义方法的逻辑:

05ca9f110ed08707c60a5f1bb63b5fe9.jpeg

2.修改成员变量:

将 UserDetailsService 变量替换成 ZltUserDetailsService

6a9e288256ca4f91661b145ca1e6882c.jpeg

同理修改相关的get和set方法:

724cff498237499902139c14b7198149.jpeg

1.3 到WebSecurityConfigurerAdapter上配置CustomAuthenticationProvider内容。

在目前系统中是 SecurityConfig 类继承了CustomAuthenticationProvider类,故在此类中配置

需注释掉原来的内容:全局用户信息部分。

47fc7c76ad3ceb9611786a2faed0ce25.jpeg

同步修改成员变量:

495c7bc05a85e5abc7b69a8df5cda60d.jpeg

配置完成后,可以请求接口进行

对应请求参数中增加userType即可。

2be0928eeb45239667d0b9be68dc2337.jpeg

2 刷新Token

对应逻辑需要同步修改,否则刷新token逻辑会调用原始loadUserByUsername

2.1 复制org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper 自定义 CustomUserDetailsByNameServiceWrapper 在 loadUserDetails() 方法添加自定义的userType

这里依然是复制整个内容,并不是继承UserDetailsByNameServiceWrapper

c66e9360918af5c87ba5ebfcc854004c.jpeg

(1)自定义loadUserDetails() 逻辑

通过相关方法,拿到userType后,即可以根据实际情况进行编码。

最后也是会调用 userDetailsService.loadUserByUsername(userName,userType);

85cd10fde3a31c5cac62eb088100e05e.jpeg

(2)修改成员变量

1926e61bef93ff0b717c01cc341f832b.jpeg

将UserDetailsService内容改为ZltUserDetailsService

(3)同理修改构造体

8c22319977aa2f1d2bfece68e1ffa938.jpeg

2.2 复制 org.springframework.security.oauth2.provider.token.DefaultTokenServices 到自定义的 CustomTokenServices 然后修改refreshAccessToken() 方法

复制整个内容:

82b8bf46379a4825f6c7af2b6b9cc2a4.jpeg

修改 refreshAccessToken 方法:

其实只改了if (this.authenticationManager != null && !authentication.isClientOnly()) 这里面的内容

主要是下面内容:

ae23c08252f6814f4f301dd4cac18804.jpeg

2.3 往认证服务器中配置刚刚自定义的两个类 CustomUserDetailsByNameServiceWrapper 和 CustomTokenServices。

这个可能有2种方式,一种是对应页面中展示的这种。

另一种是目前项目实现的这种方式。

2者的背后机制是一样的,因为最后都是将内容实例化后交给spring容器。

这里举例的是后者,因为是经过实际的验证通过的。个人感觉实际项目中这种方式也比较合理这种方式是依赖了自定义授权的基础。

在 TokenGranterConfig.java 类中:

实例化自定义的CustomTokenServices,创建createDefaultTokenServicesCustomer 方法

9dc0effb1f9c7a0bdfc4e591ec679a64.jpeg

声明CustomUserDetailsByNameServiceWrapper 类

因为这里只有声明了新增的CustomUserDetailsByNameServiceWrapper类才会调用对用的类。

c16f97f5e9070dd9aeb2e965a41cf0d7.jpeg

成员变量修改

edaa491595c5cd3621d72dc00d90cce6.jpeg

调用接口验证:

这个是在body里面:

877522c9320bb6849c97a9426b01d393.jpeg

认证内容也需要配置

c5344907025c9b8e20809bd8206429fa.jpeg

配置完进行接口即可:

d5e3b4a18add2656378ca4bc96e9b05b.jpeg
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值