前言:银行安全面试认证授权,登录登出安全开发问题。
Authentication(认证) 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。
Authorization(授权) 发生在 Authentication(认证) 之后。它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,system。有些对系统资源操作比如删除、添加、更新只能特定人才具有。
RBAC 模型通过角色关联权限,角色同时又关联用户的授权的方式。一个用户可以拥有若干角色,每一个角色又可以被分配若干权限。
创建不同的角色并为不同的角色分配不同的权限范围(菜单)。
1、Cookie
中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的基本信息给填了。
2、HTTP 协议是无状态的。使用Cookie
保存Session
或者Token
,向后端发送请求的时候带上Cookie
,后端获取Session
或者Token
记录用户当前的状态。
3、Cookie
可以用来记录和分析用户行为,将这些信息存放在Cookie
服务器获取你在某个页面的停留状态或者看了哪些商品。
1、设置Cookie
返回给客户端
@GetMapping("/change-username")
public String setCookie(HttpServletResponse response) {
// 创建一个 cookie
Cookie cookie = new Cookie("username", "Jovan");
//设置 cookie过期时间
cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days
//添加到 response 中
response.addCookie(cookie);
return "Username is changed!";
}
2、@CookieValue
注解获取特定的 cookie 的值
@GetMapping("/")
public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) {
return "Hey! My username is " + username;
}
3、读取所有的Cookie
值
@GetMapping("/all-cookies")
public String readAllCookies(HttpServletRequest request) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
return Arrays.stream(cookies)
.map(c -> c.getName() + "=" + c.getValue()).collect(Collectors.joining(", "));
}
return "No cookies";
}
1、HTTP 协议是无状态的。当你要添加商品到购物车时,系统不知道是哪个用户操作的。服务端给特定的用户创建特定的Session
之后就可以标识这个用户并且跟踪这个用户,服务器记录用户的状态。
1、用户成功登陆系统(服务器生成Session
一般会选择存放在 Redis数据库),然后返回给客户端具有 SessionID 的 Cookie。
2、当用户向后端发起请求的时候会把SessionID
带上,这样后端就知道你的身份状态。
2、服务器验证通过后,服务器为用户创建一个Session
,并将Session
信息使用Redis存储起来。
3、服务器向用户返回一个 SessionID
,写入用户的 Cookie
。
4、当用户保持登录状态时,Cookie
将与每个后续请求一起被发送出去。
5、服务器可以将存储在 Cookie
上的 SessionID
与存储在内存中或者数据库中的 Session
信息进行比较,以验证用户的状态。
1、依赖Session
的关键业务一定要确保客户端开启了Cookie。
2、注意 Session
的过期时间(一般是5分钟,退出系统应立刻消除对应的Session记录)。
3、多服务器节点 Session-Cookie。
4、Session信息服务器的可用性。
部署了两份相同的服务 A,B,用户第一次登陆的时候 ,Nginx 通过负载均衡机制将用户请求转发到 A 服务器,此时用户的 Session信息保存在 A 服务器。结果,用户第二次访问的时候 Nginx 将请求路由到 B 服务器,由于 B 服务器没有保存用户的 Session信息,导致用户需要重新进行登陆。
单独使用所有服务器都能访问的数据节点(Redis缓存)来存放Session信息。为了保证高可用,数据节点尽量避免是单节点。
将SessionID
加密之后放在请求的url
里面再传入后端与存储在内存中或者数据库中的 Session 信息进行比较
,但是安全性下降,不法分子拿到Session后进行Session分析或者是模拟请求。
进行Session
认证的时候,我们一般使用Cookie
来存储SessionId
,当我们登陆后后端生成一个SessionId
放在 Cookie 中返回给客户端,服务端通过 Redis 或者其他存储工具记录保存着这个SessionId
,客户端登录以后每次请求都会带上这个SessionId
,服务端通过这个SessionId
来判断标示你的状态。如果别人通过Cookie
拿到了SessionId
后就可以代替你的身份访问系统了。Session
认证中Cookie
中的SessionId
是由浏览器发送到服务端的,借助这个特性,攻击者就可以通过让用户误点攻击链接,达到伪装攻击请求(携带Cookie信息)效果。
使用Token
的话就不会存在这个问题,在我们登录成功获得Token
之后,一般会存放localStorage
(浏览器本地存储)中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个Token
,这样就不会出现 CSRF 漏洞的问题。因为,即使有个你点击了非法链接发送了请求到服务端,这个非法请求是不会携带Token
的,所以这个请求将是非法的。
不论是Cookie还是Token都无法避免跨站脚本攻击(Cross Site Scripting)XSS,通过脚本盗用信息比如Cookie
Token 不需要自己存放Session
信息就能实现身份验证的方式,服务器端就不需要保存Session
数据了,只用在客户端保存服务端返回给用户的Token
1、Session
信息需要保存一份在服务器端需要我们保证保存Session
信息服务器的可用性。
2、不适合移动端(依赖Cookie
)。
Token
的类型。Payload
、Header
和一个密钥(secret
)使用 Header
里面指定的签名算法(默认是 HMAC SHA256)生成。基于 Token 进行身份验证的的应用程序中,服务器通过Payload
、Header
和一个密钥(secret
)创建令牌(Token
)并将Token
发送给客户端。客户端将Token
保存在 Cookie 或者 localStorage 里面。以后客户端发出的所有请求都会携带这个令牌。可以把它放在 Cookie 里面自动发送,但是这样不能跨域。更好的做法是放在 HTTP Header 的 Authorization 字段中:Authorization: Bearer Token
1、无状态:token 自身包含了身份验证所需要的所有信息,使得我们的服务器不需要存储 Session 信息,大大减轻了服务端的压力。由于token 的无状态,也导致了它最大的缺点。即当后端在token有效期内废弃一个token或者更改它的权限的话,不会立即生效,一般需要等到有效期过后才可以。另外,当用户Logout 的话,token也还有效。除非,我们在后端增加额外的处理逻辑。
2、有效避免了跨站请求伪造(CSRF) 攻击:在我们登录成功获得Token之后,一般会选择存放localStorage(浏览器本地存储)中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个Token,这样就不会出现 CSRF 漏洞的问题。因为,即使有个你点击了非法链接发送了请求到服务端,这个非法请求是不会携带Token的,所以这个请求将是非法的。大部分情况下Token存放在 localstorage下都是最好的选择。
3、适合移动端应用:使用Session 进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到 Cookie(需要 Cookie 保存 SessionId),所以Session 不适合移动端。token可以被客户端存储就能够使用,而且 token还可以跨语言使用。
4、单点登录友好:使用 Session 进行身份认证的话,实现单点登录,需要我们把用户的Session 信息保存在Redis服务器上,并且还会遇到常见的Cookie跨域的问题。使用token进行认证的话, token被保存在客户端,不会存在服务器保存Session信息问题。HTTP Header的Authorization字段解决跨域问题。
1、注销登录(退出登录,修改密码,服务端修改了某个用户具有的权限或者角色,用户的帐户被删除/暂停,用户由管理员注销)场景下 token 还有效问题:问题不存在于Session 认证方式中,因为在 Session 认证方式中,遇到这种情况的话服务端删除对应的 Session 记录即可。使用 token 认证的方式就不好解决了,token一旦派发出去,如果后端不增加其他逻辑的话,它在失效之前都是有效的。
最佳实践:
token 存入内存数据库:token 存入redis 内存数据库。如果需要让某个 token 失效就直接从 redis 中删除这个token。导致每次使用 token发送请求都要先从DB中查询 token 是否存在的步骤,而且违背了 JWT 的无状态原则。
黑名单机制:redis内存数据库维护一个黑名单。如果想让某个 token 失效的话就直接将这个 token 加入到黑名单,每次使用 token 进行请求的话都会先判断这个 token 是否存在于黑名单中。
修改密钥:为每个用户都创建一个专属密钥,如果我们想让某个 token 失效,我们直接修改对应用户的密钥。但是存在以下问题:(1)如果服务是分布式的,每次发出新的 token 时都必须在多台服务器上同步密钥。你需要将密钥存储在数据库或其他外部服务中,这样和 Session 认证就没太大区别。 (2) 如果用户同时在两个浏览器打开系统,或者在手机端也打开了系统,如果它从一个地方将账号退出,那么其他地方都要重新进行登录,这是不可取的。
保持令牌的有效期限短并经常轮换:导致用户登录状态不会被持久记录,而且需要用户经常登录。
用户名/密码哈希值:使用用户的用户名/密码的哈希值对 token 进行签名。如果用户名/密码更改,任何先前的令牌将自动无法验证。
2、token续签问题:token过期后如何认证,如何实现动态刷新 token,避免用户经常需要重新登录。
最佳实践:
类似Session认证:假设服务端给的 token 有效期设置为30分钟,服务端每次进行校验时,如果发现 token 的有效期马上快过期了,服务端就重新生成 token给客户端。客户端每次请求都检查新旧token,如果不一致,则更新本地的token。
每次请求都返回新token:客户端每次请求资源都生成新的token,开销会比较大。
token有效期设置到半夜:保证了大部分用户白天可以正常登录,适用于对安全性要求不高的系统。
用户登录返回两个token:第一个是 accessToken ,它的过期时间 token 本身的过期时间比如为半个小时,另外一个是 refreshToken它的过期时间更长一点比如为1天。客户端登录后,将 accessToken和refreshToken 保存在本地,每次访问将 accessToken 传给服务端。服务端校验 accessToken 的有效性,如果过期的话,就将 refreshToken 传给服务端。如果有效,服务端就生成新的 accessToken 给客户端。否则,客户端就重新登录。但是存在以下问题:(1)需要客户端来配合。(2)用户注销的时候需要同时保证两个 token 都无效。(3)重新请求获取 token 的过程中会有短暂 token不可用的情况
总结:JWT 最适合的场景是不需要服务端保存用户状态的场景,如果考虑到 token注销和 token续签的场景话,没有特别好的解决方案,大部分解决方案都给token加上了状态,有点类似Session认证。
SSO(Single Sign On) 单点登录,用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。
登陆了京东金融之后,同时也成功登陆京东的京东超市、京东国际、京东生鲜等子系统。
1、用户角度:用户能够做到一次登录多次使用,无需记录多套用户名和密码。
2、系统管理员角度:管理员只需维护好一个统一的账号中心。
3、新系统开发角度:新系统开发时只需直接对接统一的账号中心。
功能模块 | 说明 |
---|---|
系统站点 | 需要登录的站点 |
SSO站点-登录 | 提供登录的页面 |
SSO站点-登出 | 提供注销登录的入口 |
SSO服务-登录 | 提供登录服务 |
SSO服务-登录状态 | 提供登录状态校验/登录信息查询的服务 |
SSO服务-登出 | 提供用户注销登录的服务 |
数据库 | 存储用户账户信息 |
缓存 | Redis存储用户的登录状态信息 |
对象 | 说明 |
---|---|
AuthToken | 直接使用UUID/GUID,如果有验证AuthToken合法性需求,可以将UserName+时间戳加密生成,服务端解密之后验证合法性。 |
登录信息 | 通常是将UserID,UserName缓存起来。 |
解决Cookie不能跨域的核心思路:
登录完成之后通过回调的方式,将AuthToken传递给主域名之外的站点,该站点自行将AuthToken保存在当前域下的Cookie中。
登出完成之后通过回调的方式,调用非主域名站点的登出页面,完成设置Cookie中的AuthToken过期的操作。
OAuth 是行业的标准授权协议,用来授权第三方应用获取有限的权限。为第三方应用颁发一个有时效性的令牌 Token,使得第三方应用能够通过该令牌获取相关的资源。
应用网站接入了第三方登录的一般使用 OAuth 2.0 协议。例如:微信支付、支付宝支付。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。