一、Session与Token使用之传统架构模式与前后端分离架构模式对比
1、传统的应用架构方式,将用户信息保存在服务器session中,用户访问时,通过cookie中存放的sessionId访问应用服务器,从而来获取用户信息,见图(1)
2、随着技术的不断升级发展,新颖的前端技术出现,如app或者前后端分离的架构方式,用户现在不是直接通过浏览器访问应用服务器,而是通过第三方应用(如APP、WebServer【如Node.js环境】,此时的第三方应用就是我们使用前后分离编写的前端应用)去访问应用服务器,如图(2)。这种架构下使用cookie、session的方式会出现一些问题。
问题1:开发繁琐,cookie是浏览器内建好的,但是在app这种架构下需要自己处理cookie相关逻辑
问题2:安全性和客户体验差 发送一个请求,如果cookie里面有JSsesionId,我直接就从session里面获取信息了,就认为你登录了;此时如果的你的JSessionId别人知道了,放到cookie中去发请求就可以获取到用户信息了。这样一来就不安全了,如果把session过期时间设置变短,用户体验又变差了 但是使用token的方式,我们可以增强token字符串的复杂度 还可以将token的过期时间设置较短使用刷新token来保证安全性和客户体验性
问题3:有些前端技术不支持cookie,如小程序
由于这些问题,App、前后端分离这种架构模式下采用token(令牌)的方式存放用户的认证信息 也就是说认证、授权的方式不再是基于应用服务器的session了,而是基于token
3、Spring Security OAuth框架引入,主要实现了一个服务提供商(provider)的角色,包括认证服务器和资源服务器,见图(3)
二、实现标准的OAuth服务提供商
认证服务器:
添加以下两个注解就是默认的认证服务器:
@Configuration
@EnableAuthorizationServer
1、授权码模式(Authorization code Grant)
1)、将用户导向认证服务器,获取授权码
发送GET请求:
http://localhost:8060/oauth/authorize?response_type=code&client_id=jacob&redirect_uri=http://www.baidu.com&scope=all
参数说明:
response_type:何种授权模式
client_id:哪个应用请求授权,每个应用都会分配一个clientId
redirect_uri:回调地址,用户授权后用来返回授权码
scope:权限范围
注意:弹出basic认证框输入用户名密码,表示是由系统中的哪个用户进行授权的
实现UserDetailsService返回用户信息的时候需要一个ROEL_USER的角色
返回结果:https://www.baidu.com/?code=O21JMa
2)、根据获取到的授权码去获取access_token
发送POST请求:
http://localhost:8060/oauth/token
请求头:Authentication:clientId、clientSecret
请求参数:
grant_type=authorization_code code=O21JMa client_id=jacob redirect_uri=http://www.baidu.com scope=all
返回:
{
"access_token": "35811a90-092a-4eff-a1f4-022ece6ad74c",
"token_type": "bearer",
"refresh_token": "c99f3aa3-26af-44c0-a52b-6d8bf40ce8ab",
"expires_in": 43199,
"scope": "all"
}
2、密码模式(Resource Owner Password Credentials Grant)
使用用户名密码去获取Token
发送POST请求:
http://localhost:8060/oauth/token
请求头:Authentication:clientId、clientSecret
请求参数:
grant_type=password username=root password=123456 scope=all
返回:
{
"access_token": "35811a90-092a-4eff-a1f4-022ece6ad74c",
"token_type": "bearer",
"refresh_token": "c99f3aa3-26af-44c0-a52b-6d8bf40ce8ab",
"expires_in": 42416,
"scope": "all"
}
注意:使用密码模式获取token是有局限性的,如果第三方应用不是完全可信任的,那么就可能存在风险
3、简化模式(Implicit Grant)
1)、获取token,参数和申请授权码类似,client_id,redirect_uri回调地址,response_type有变动,改为直接获取token,scope权限,state用于认证标记,传过去什么回调时传回来什么
发送GET请求:
2)、根据返回的access_token,进行接口调用,访问API
4、客户端模式(Client Credentials Grant)
1、发送POST请求获取token
http://localhost:8060/oauth/token
请求头:Authentication:clientId、clientSecret
请求参数:
grant_type=client_credentials scope=all
返回结果:
{
"access_token": "6261f160-9814-426f-b339-308c8203ebdf",
"token_type": "bearer",
"expires_in": 43199,
"scope": "all"
}
2、根据返回的access_token,进行接口调用,访问API
3、使用场景:
1)、客户端模式应用于应用程序想要以自己的名义与授权服务器以及资源服务器进行互动。
2)、例如使用了第三方的静态文件服务
5、刷新TOKEN
1)、以授权码为例,获取token的同时返回了refresh_token
{
"access_token": "bba45bb4-ef83-4f8d-8461-4e545a49c94f",
"token_type": "bearer",
"refresh_token": "8205faa5-8bd3-44f5-8cf0-7095c26586d1",
"expires_in": 43199,
"scope": "all"
}
2)、在token过期后,携带refresh_token发送POST请求获取新的token
http://localhost:8060/oauth/token
请求头:Authentication:clientId、clientSecret
请求参数:
grant_type=refresh_token refresh_token=8205faa5-8bd3-44f5-8cf0-7095c26586d1
3)、可以拿新的access_token继续访问资源了
4)、建议将access_token和refresh_token的过期时间保存下来,每次调用平台方的业务api前先对access_token和refresh_token进行一下时间判断,如果过期则执行刷新access_token或重新授权操作。refersh_token如果过期就只能让用户重新授权。
资源服务器:
添加以下两个注解就是默认的资源服务器:
@Configuration
@EnableResourceServer
资源服务器解释:当然是用来保护我们所编写的Rest服务,他是通过Spring Security过滤器链来保护的,在过滤器链上添加一个OAuth2AuthenticationProcessingFilter过滤器,该过滤器会判断请求中携带的token是否有权限访问rest服务
注:1、这里的认证服务器和资源服务器是逻辑概念,可以是一台服务器也可以是两台服务器
2、使用SpringSecurityOAuth提供的默认认证流程,生成的token是存在内存中的