需求
首先如果信息不需要根据用户隔离则不需要登录,所有人都看到一样的内容拥有一样的权限享有同样的资源即可,假设一个系统中一些信息需要根据不同用户实现信息隔离,需要一种机制实现对用户的认证。
交互协议
在web系统中使用HTTP协议进行通信,HTTP协议是一种无状态的协议,当一个请求响应与另一个请求响应之间没有联系(1.0短连接)。
如何维护状态(单体系统)
但是系统需要对用户身份进行判断,在单体系统中,在浏览器与服务器间使用了cookie session机制,在浏览器向服务器首次发起请求时服务器会分配一块空间记录与浏览器会话的内容,这个空间叫session,还会生成一个sessionid传给浏览器(set-cookie),服务器委托给浏览器生成一块空间保存信息,浏览器上这个空间叫cookie,首次接收响应后根据set-cookie内容保存sessionid在浏览器中,之后在访问就携带上这个cookie,服务器根据cookie中的sessionid找到session就实现了浏览器与服务器之间会话的状态保持。
用户登录后就在session中存放一个字段类似“已登陆”这样,当用户下次再访问服务器就从cookie中查看到底这个连接有没有用户登录的状态。
cookie
可以设置过期时间,所属域名,所属路径,支持协议http/https
为什么cookie能存信息还要用session?
cookie浏览器限制有大小,再有私密信息放在cookie也就是浏览器本地可以被别人从本地查看,仅保留sessionid的话私密信息就存在服务器,别人就不能从本地找出来了。
session
session是由容器管理的比如tomcat、jetty,默认过期时间30min,存储在一个concurrentHashmap的数据结构中。
分布式系统
分布式session
由于session保存在容器中,那么分布式系统里存在多个容器,容器之间本身不共享session,对于一个服务器来说自己有session其他服务器就没有了。为了解决这个问题spring框架提供了spring session将session存在服务器外部的一块空间,大家都根据sessionid从这个空间查,可用的空间可以用redis实现。
Token
虽然分布式session已经实现了服务器间共享会话状态的功能,但是效率降低了,每一次请求都需要先从redis中取出session来查询用户信息。
使用Token可以减少一次解析的动作,当用户登陆时生成一个Token(随机字符串)发放给浏览器并存在redis(设置过期时间),浏览器之后请求带上token,服务器收到token直接去查redis有没有这个token,如果有则直接认为该用户是已经登陆的状态,而不用再反序列化session解析内容。
但是这样token仅仅携带登陆与否的信息而没有其他信息,比如我们系统中要实现登录后权限的分级,要有青铜、白银、黄金来给用户展示不同的资源,那么仅仅判断有没有登录就不够了,于是催生了JWT(JSON WEB TOKEN)。
JWT
JWT是一个JSON字符串,有头部、信息荷载、签名组成。
header
头部一般有加密算法、令牌类型。
payload
荷载中有签发人、过期时间、主题、受众、生效时间、签发时间、编号。自己也可以加入自定义字段比如用户名、角色。
signature
该部分对前两部分进行数据签名,使用私钥加密得到加密后内容就是这个签名。服务器收到后用公钥验证jwt。
传输时还用对JWT使用base64编码。
在登陆时我们就将用户的权限信息放在payload中,服务器收到了使用公钥解析就知道这个用户是否登陆过又有何种角色。
安全性
如果说别人想窃取可以使用https协议传输中能保证安全,如果自己想改变那末尾有签名信息自己改了服务器就不认了。
实践方案
我自己做的微服务项目使用JWT方式鉴权认证,使用Spring Security与OAuth2框架,那用户权限信息使用RBAC模型(用户、角色、权限)。
具体方案
认证有认证服务器,鉴权有资源服务器。把认证作为单独服务,鉴权放在网关,这样其他服务器就只管提供服务。
认证服务
认证服务引入提到两个框架的依赖,实现UserDetailsService接口,把用户、角色信息注入SecurityUser,继承OAuth2的配置类AuthorizationServerConfigurerAdapter
主要配置
- ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
- AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
- AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
资源服务器(网关)
在网关配置中注入了自定义的ReactiveAuthorizationManager用于权限判断,SecurityWebFilterChain中添加JWT鉴权的配置以及相应的处理