Spring Security OAuth2.0认证授权六:前后端分离下的登录授权
历史文章
Spring Security OAuth2.0认证授权一:框架搭建和认证测试
Spring Security OAuth2.0认证授权二:搭建资源服务
Spring Security OAuth2.0认证授权三:使用JWT令牌
Spring Security OAuth2.0认证授权四:分布式系统认证授权
Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
本篇文章将会解决上一篇文章《Spring Security OAuth2.0认证授权五:用户信息扩展到jwt 》中遗留的问题,并在原有的项目中新增模块business-server用来充当前端页面的web容器并转发登录请求和更换token的请求等,以模拟前后端分离下的登录以及更换token操作。
一、jwt令牌在网关处的过期时间校验
上一篇文章中讲了在网关处解析token并转发到目标服务的操作,因为使用了jwt令牌的原因,所以省了一步到认证服务器认证的操作,只要验签成功,就认为令牌有效。这实际上留下了一个bug:服务端无法主动取消jwt令牌,所以这个令牌只要客户端保存下来,如果不调用认证服务器的令牌验证接口,这个jwt令牌将永远有效。因此需要在网关处加上对过期时间的校验。
在TokenFilter中添加以下代码逻辑
//取出exp字段,判断token是否已经过期
try {
Map<String, Object> map = objectMapper.readValue(payLoad, new TypeReference<Map<String, Object>>() {
});
long expiration = ((Integer) map.get("exp")) * 1000L;
if (expiration < new Date().getTime()) {
return unAuthorized(exchange, "未认证的请求:token存在,但是已经失效",WrapperResult.TOKEN_EXPIRE);
}
} catch (IOException e) {
log.error("", e);
return unAuthorized(exchange, "未认证的请求:错误的token",null);
}
二、refresh-token接口缺少用户信息
refresh-token在access_token过期,但是refresh-token未过期的时候使用,目的是使用refresh_token更新已经过期的access_token,这样理论上来说,客户端只要能在refresh_token过期之前进行任意操作,就可以避免重新登录了。
上一篇文章中将用户信息放到了jwt token中并返回给客户端,但是如果使用refresh_token更新token,后端会报错,前端取到的token中则缺少了用户信息。究其原因,和JwtAccessTokenConverter有关系,关于这个类的实例,当初创建的方法如下
@Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//对称秘钥,资源服务器使用该秘钥来验证
return jwtAccessTokenConverter;
}
这里的new操作省了很多默认参数的指定,且先看下为啥会缺少用户信息,扩展用户信息的关键在于方法com.kdyzm.spring.security.auth.center.service.MyUserDetailsServiceImpl#loadUserByUsername,这里扩展了用户信息,使其从单纯的username字符串变成了UserDetailsExpand对象,然后在增强方法com.kdyzm.spring.security.auth.center.enhancer.CustomTokenEnhancer#enhance中将扩展信息取出来放到Token中。
经过debug,发现

最终发现是如下代码的问题org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter#extractAuthentication
public Authentication extractAuthentication(Map<String, ?> map) {
if (map.containsKey(USERNAME)) {
Object principal = map.get(USERNAME);
Collection<? extends GrantedAuthority> authorities = getAuthorities(map);
//运行到这里的时候userDetailsService为空,所以并没有执行自定义的loadUserByUsername方法
if (userDetailsService != null) {
UserDetails user = userDetailsService.loadUserByUsername((String) map.get(USERNAME));
authorities = user.getAuthorities();
principal = user;
}
return new UsernamePasswordAuthenticationToken(principal, "N/A", authorities);
}
return null;
}
层层网上追寻调用链,竟然是JwtAccessTokenConverter创建的时候省略参数导致的,只需要如此做就可以解决问题了
@Bean
public JwtAccessTokenConverter accessTokenConverter(){
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
DefaultAccessTokenConverter tokenConverter = new DefaultAccessTokenConverter();
DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
userTokenConverter.setUserDetailsService(userDetailsService);
tokenConverter.setUserTokenConverter(userTokenConverter);
jwtAccessTokenConverter.setAccessTokenConverter(tokenConverter);
jwtAccessTokenConverter.setSigningKey(SIGNING_KEY);//对称秘钥,资源服务器使用该秘钥来验证
return jwtAccessTokenConverter;
}
JwtAccessTokenConverter对象创建的时候指定DefaultUserAuthenticationConverter使用的userDetailsService即可。
三、新建business-server模块作为web容器
这里新建的business-server模块有两个功能
- 充当web容器,该服务并没有使用模板化技术,使用的是纯html、css实现前端
- 转发前端登录、更换token请求
可能会有人对第二条有疑问,为什么要这么做?之前测试的时候基本上都是使用postman发起的请求,请求的方式是这样的http://127.0.0.1:30000/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=zhangsan&password=123可以看到这里传递了很重要的参数client_id和client_secret,这两个参数无论如何也不应当泄露给前端,通常都是中间的真正的客户端服务拼接这两个参数再将请求转发给认证服务
四、前后端分离
设计上想要实现以下功能
- 首页未登录则提示用户登录,已经登录则展示用户个人信息
- 用户登录之后将令牌保存到localStorage
- token过期之后用户可以选择使用refresh_token更换已经过期的令牌(access_token)
- 已经过期的refresh_token不能用于更换新的令牌
1、关闭认证服务表单登录
以前请求认证服务的任意接口,如果没有认证,则都会跳转到系统自带的登录页面,现在我们想要实现前后端分离了,原来系统自带的登录页面就有些碍眼了,直接关闭就好。关闭方法如下,spring security的配置更改为如下:
.formLogin()
.disable();
2、前后端代码
前端代码在business-server/src/main/resources/static目录下,只有两个页面,一个首页,一个登陆页面
后端只有两个接口
- 登录接口:com.kdyzm.spring.security.oauth.study.business.server.controller.LoginController#login
- 更新token接口:com.kdyzm.spring.security.oauth.study.business.server.controller.TokenController#refreshToken
其它不做赘述,不过前端页面写起来挺麻烦的。。难是不难的
五、测试
源代码:
测试前首先需要重新执行初始化sql(auth-server/docs/sql/init.sql),然后依次启动 register-server、gateway-server、auth-server、resource-server、business-server 五个服务
启动成功后打开浏览器,输入http://127.0.0.1:30002/地址,就会看到以下页面

点击登录之后,出现登录框

输入账号密码之后,登录成功之后会跳转首页,就会看到个人信息

这里设置的token有效期为10秒,所以很快token就会失效,十秒钟之后刷新页面就会有新的提示

接下来可以有两种选择,一种是使用refresh-token更新失效的令牌,另外一种是重新登录,这里refresh_token的有效期也很短,只有30秒,如果超出30秒,则会更新失败,提示如下

而如果在30秒内刷新令牌,则会重新获取到令牌并刷新当前页
六、源代码地址
源代码地址:https://gitee.com/kdyzm/spring-security-oauth-study/tree/v7.0.0
我的博客地址:https://blog.kdyzm.cn/
Spring Security OAuth2.0认证授权六:前后端分离下的登录授权的更多相关文章
- Spring Security OAuth2.0认证授权四:分布式系统认证授权
Spring Security OAuth2.0认证授权系列文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授 ...
- Spring Security OAuth2.0认证授权三:使用JWT令牌
Spring Security OAuth2.0系列文章: Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二: ...
- Spring Security OAuth2.0认证授权二:搭建资源服务
在上一篇文章[Spring Security OAuth2.0认证授权一:框架搭建和认证测试](https://www.cnblogs.com/kuangdaoyizhimei/p/14250374. ...
- Spring Security OAuth2.0认证授权五:用户信息扩展到jwt
历史文章 Spring Security OAuth2.0认证授权一:框架搭建和认证测试 Spring Security OAuth2.0认证授权二:搭建资源服务 Spring Security OA ...
- spring security简单教程以及实现完全前后端分离
spring security是spring家族的一个安全框架,入门简单.对比shiro,它自带登录页面,自动完成登录操作.权限过滤时支持http方法过滤. 在新手入门使用时,只需要简单的配置,即可实 ...
- Spring Security OAuth2.0认证授权一:框架搭建和认证测试
一.OAuth2.0介绍 OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不 需要将用户名和密码提供给第三方应用或分享他们数据的所有内容. 1.s ...
- Session与Token认证机制 前后端分离下如何登录
字号 1 Web登录涉及到知识点 1.1 HTTP无状态性 HTTP是无状态的,一次请求结束,连接断开,下次服务器再收到请求,它就不知道这个请求是哪个用户发过来的.当然它知道是哪个客户端地址发过来的 ...
- Spring security OAuth2.0认证授权学习第四天(SpringBoot集成)
基础的授权其实只有两行代码就不单独写一个篇章了; 这两行就是上一章demo的权限判断; 集成SpringBoot SpringBoot介绍 这个篇章主要是讲SpringSecurity的,Spring ...
- Spring security OAuth2.0认证授权学习第三天(认证流程)
本来之前打算把第三天写基于Session认证授权的,但是后来视屏看完后感觉意义不大,而且内容简单,就不单独写成文章了; 简单说一下吧,就是通过Servlet的SessionApi 通过实现拦截器的前置 ...
随机推荐
- C# 将json字符串进行排序 转成键值
public static string StortJson(string json) { var dic = JsonConvert.DeserializeObject<SortedDicti ...
- Spring Boot使用Maven自定义打包方式
前言:本文将告诉你如何将程序Jar与与依赖Jar及配置文件分离打包,以下列举了两种不同Maven打包方式,其打包效果一致! 一.第一种Maven打包方式,将jar及resources下全部配置文件,拷 ...
- 使用JMeter进行压力测试
使用JMeter进行压力测试(基础篇) 1.启动Jmeter 双击图中所示文件出现如下图所示即启动成功 2 新建测试计划 File------>new 2.1右键新建线程组 add-----&g ...
- 分析http协议和高并发网站架构
案例任务名称 分析http协议和高并发网站架构 案例训练目标 深入理解http协议的工作原理 掌握http协议的分析方法 包含技能点 搭建web服务器 编辑简单的html页面并上传到服务器 使用wir ...
- Kubernetes K8S之通过helm部署metrics-server与HPA详解
Kubernetes K8S之通过helm部署metrics-server与 Horizontal Pod Autoscaling (HPA)详解 主机配置规划 服务器名称(hostname) 系统版 ...
- 腾讯IOT安卓开发初探
目录 腾讯IOT 安卓开发初探 Tecent IOT 开发平台的使用 新建项目 创建产品 添加自定义功能 设备开发 微信小程序配置 面板配置 新建设备 使用设备 安卓开发 前置配置 data.json ...
- Hbase之过滤器的使用
一.过滤器概念 基础API中的查询操作在面对大量数据的时候是非常物无力的,这里Hbase提供了高级的查询方法:Filter(过滤器).过滤器可以根据簇.列.版本等更多的条件来对数据进行过滤,基于Hba ...
- 使用 Admission Webhook 机制实现多集群资源配额控制
1 要解决的问题 集群分配给多个用户使用时,需要使用配额以限制用户的资源使用,包括 CPU 核数.内存大小.GPU 卡数等,以防止资源被某些用户耗尽,造成不公平的资源分配. 大多数情况下,集群原生的 ...
- MySQL 集群知识点整理
随着项目架构的不断扩大,单台 MySQL 已经不能满足需要了,所以需要搭建集群将前来的请求进行分流处理.博客主要根据丁奇老师的专栏<<MySQL实战45讲>>学习的总结. 架构 ...
- 【剑指 Offer】10-I.斐波那契数列
题目描述 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项.斐波那契数列的定义如下: F(0) = 0, F(1) = 1 F(N) = F(N - 1) + F(N - ...