单点登录SSO
# 什么是单点登录
单点登录的英文名叫做:Single Sign On(简称SSO)。
- 用人话说:就是在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
# 会话状态及存储
会话状态:客户端和服务端之间通信过程产生的状态信息。
在开始做单点登录之前,咱们需要理解有无状态会话机制及存储机制,值得注意的是有无状态没有绝对的好坏, 需要根据实际情况来设计,当然也可以是结合使用。
会话存储:用于存储登录明文或密文信息。
名称 | 只读 | 说明 |
---|---|---|
Local Storage | 是 | 允许在浏览器中存储 key/value 对的数据, 用于长久保存整个网站的数据,保存的数据没有过期时间,直到手动去删除。 |
Session Storage | 否 | 允许在浏览器中存储 key/value 对的数据, 用于临时保存同一窗口(或标签页)的数据,在关闭窗口或标签页之后将会删除这些数据。 |
Cookie | 否 | 服务端、客户端都可以创建,存储在浏览器 Cookies 中,分为会话 cookie (浏览器关闭失效) 和持久化 cookie (到期失效)。 |
Session | 是 | 服务端创建,储存在服务端,可以存储大量的数据(默认生命周期为30分钟),以会话 cookie 存储 sessionId 状态通信 |
# 有状态会话
普通登录逻辑
- 1,用户登录
- 2,服务端设置 session
- 3,设置会话 cookie 存储 sessionId
- 4,用户访问系统资源
- 5,根据会话 sessionId 找到 web 容器中的 session 登录信息
单点登录逻辑
- 1,用户登录 Nginx 负载均衡路由
- 2,登录某个节点
- 3,web 容器存储会话 session 至分布式缓存 Redis
- 4,用户访问系统资源
- 5,Nginx 路由至其它节点
- 6,web 容器从分布式缓存 Redis 中读取 session 登录信息
# 无状态会话
无状态利用的是加密技术,换人话就是 token 登录票据中存储了登录关键信息,后端解密直接得到登录信息。
- 1,用户登录
- 2,Nginx 路由至任意节点
- 3,生成加密 token 票据
- 4,存储在浏览器端,可以是 cookie 也可以是 Local Storage 中存储
- 5,用户访问系统资源,需要携带登录 token 票据
- 6,任意节点 sso 客户端会解密 token 票据获取登录信息
注意
- 浏览器安全策略限制,不同ip、不同端口、不同域名 都属于跨域。
- 请必须理解会话存储 LocalStorage、SessionStorage、Cookie、Session 的区别和作用。
- 请必须理解有无状态会话的区别,去思考如何应用到自己系统中。
# kisso
kisso = cookie sso 它是一把快速开发 java Web 登录系统(SSO)的瑞士军刀。
Kisso 采用 jwt 生成 token 票据,轻薄封装设计上摒弃客户端服务端模式一体式设计。 只需要引用一个依赖即可一行代码完成单点登录逻辑。
# 安装依赖
https://search.maven.org/search?q=g:com.baomidou
- Apache Maven
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>kisso</artifactId>
<version>3.8.1</version>
</dependency>
- Gradle DSL
implementation("com.baomidou:kisso:3.8.1")
# 测试 demo
- kisso-spring-boot (opens new window)
- kisso_springmvc (opens new window)
- kisso_jfinal (opens new window)
- kisso_crossdomain (opens new window)
# 使用文档
// 生成 jwt 票据,访问请求头设置‘ accessToken=票据内容 ’ 适合前后分离模式单点登录
String jwtToken = SSOToken.create().setId(1).setIssuer("admin").setOrigin(TokenOrigin.HTML5).getToken();
// 解析票据
SSOToken ssoToken = SSOToken.parser(jwtToken);
// Cookie 模式设置
SSOHelper.setCookie(request, response, new SSOToken().setId(String.valueOf(1)).setIssuer("admin"));
// 登录权限拦截器类 SSOSpringInterceptor
// 注解不拦截 @LoginIgnore
// yml 配置 kisso.config....
- Spring Boot
@ControllerAdvice
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// SSO 授权拦截器
SSOSpringInterceptor ssoInterceptor = new SSOSpringInterceptor();
ssoInterceptor.setHandlerInterceptor(new LoginHandlerInterceptor());
registry.addInterceptor(ssoInterceptor).addPathPatterns("/**").excludePathPatterns("/v1/sso/**");
}
}
# 默认 HS512 算法
// HS512 密钥,配置参数 kisso.config.sign-key
SSOHelper.getHS512SecretKey()
# 切换 RS512 算法
- 1,配置算法 kisso.config.sign-algorithm = RS512
- 2,配置私钥公钥证书,默认放置 resources 目录即可
// RSA 密钥,配置参数 kisso.config.rsa-jks-store
// 其它参数 CN=Server,OU=Unit,O=Organization,L=City,S=State,C=US
// RSA 生成 jks 密钥
$ keytool -genkeypair -alias jwtkey -keyalg RSA -dname "CN=llt" -keypass keypassword -keystore key.jks -storepass jkspassword
// RSA 生成证书
// RSA 公钥,配置参数 kisso.config.rsa-cert-store
$ keytool -export -alias jwtkey -file public.cert -keystore key.jks -storepass jkspassword
# 常见安全策略
Secure 标记为 Secure 的 Cookie 只应通过被HTTPS协议加密过的请求发送给服务端。使用 HTTPS 安全协议,可以保护 Cookie 在浏览器和 Web 服务器间的传输过程中不被窃取和篡改。
HTTPOnly 设置 HTTPOnly 属性可以防止客户端脚本通过 document.cookie 等方式访问 Cookie,有助于避免 XSS 攻击。
SameSite SameSite 属性可以让 Cookie 在跨站请求时不会被发送,从而可以阻止跨站请求伪造攻击(CSRF)。
SameSite 可以有下面三种值:
1、Strict仅允许一方请求携带 Cookie,即浏览器将只发送相同站点请求的 Cookie,即当前网页 URL 与请求目标 URL 完全一致。
2、Lax允许部分第三方请求携带 Cookie
3、None无论是否跨站都会发送 Cookie
造成现在无法获取cookie是因为之前默认是 None 的,Chrome80 后默认是 Lax
- 安全配置如下:
kisso:
config:
# 开启 https 有效,传输更安全
cookie-secure: true
# 防止 XSS 防止脚本攻击
cookie-http-only: true
# 防止 CSRF 跨站攻击
cookie-same-site: Lax
# 加密算法 RSA
sign-algorithm: RS512
...
- 配置 config 属性说明
名称 | 默认 | 说明 |
---|---|---|
encoding | UTF-8 | 编码格式 |
signKey | 空 | 签名密钥(用于对称算法) |
signAlgorithm | HS512 | 签名算法 |
rsaJksStore | key.jks | RSA 私钥存储路径(开启 RSA 算法有效) |
rsaCertStore | public.cert | RSA 公钥存储路径(开启 RSA 算法有效) |
rsaAlias | jwtkey | RSA 密钥 Alias(开启 RSA 算法有效) |
rsaKeypass | llTs1p68K | RSA 密钥 keypass(开启 RSA 算法有效) |
rsaStorepass | llTs1p68K | RSA 密钥 storepass(开启 RSA 算法有效) |
accessTokenName | accessToken | 访问票据名(访问票据在 Header 中默认名称) |
cookieName | uid | cookie 名称(无状态 cookie 模式有效) |
cookieDomain | 空 | cookie 所在有效域名,不设置为当前访问域名(无状态 cookie 模式有效) |
cookiePath | / | cookie 路径(无状态 cookie 模式有效) |
cookieSecure | / | cookie 路径(无状态 cookie 模式有效) |
cookieSecure | false | cookie 是否设置安全,设置 true 那么只能为 https 协议访问(无状态 cookie 模式有效) |
cookieHttpOnly | true | cookie 是否为只读状态,设置 js 无法获取、预防脚本攻击(无状态 cookie 模式有效) |
cookieMaxAge | -1 | cookie 有效期 -1 关闭浏览器失效(无状态 cookie 模式有效) |
cookieSameSite | 空 | cookie的SameSite属性用来限制第三方Cookie,从而减少安全风险(防止CSRF) 支持三种模式:Strict 仅允许一方请求携带Cookie,即浏览器将只发送相同站点请求的Cookie,即当前网页URL与请求目标URL完全一致,浏览器默认该模式。、Lax 允许部分第三方请求携带Cookie、None 无论是否跨站都会发送Cookie(无状态 cookie 模式有效) |
cookieBrowser | false | 是否验证 token 设置时浏览器信息 |
cookieCheckIp | -1 | 是否验证 token 设置时浏览器信息 |
loginUrl | 空 | 登录地址(设置后拦截器自动跳转) |
logoutUrl | 空 | 退出地址(设置后拦截器自动跳转) |
paramReturnUrl | 参数 ReturnURL | 登录成功回调地址(设置后拦截器自动跳转) |
cacheExpires | -1 | 缓存有效期设置 |
ssoToken | null | 访问票据 |
permissionUri | false | 权限认证 |
pluginList | null | 插件列表 |
cache | null | SSO 缓存 |
authorization | null | SSO 权限授权 |
注意
- 原则上建议采用 RS512 算法,偷懒者除外。
- Cookie 模式也是依赖 Jwt 生成登录票据,不同在于存储在 Cookie 可以利用 Cookie 的浏览器自动携带及子域名跨域实现无状态单独登录。
- 跨域是单独登录的拦路虎,必须掌握利用 LocalStorage、SessionStorage、Cookie、Session 解决多应用间的互信。
上次更新: 2022/03/26, 00:10:16