认证授权

2022-11-11 14:44:47 浏览数 (1)

前言:银行安全面试认证授权,登录登出安全开发问题。

认证

Authentication(认证) 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。

授权

Authorization(授权) 发生在 Authentication(认证) 之后。它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,system。有些对系统资源操作比如删除、添加、更新只能特定人才具有。

RBAC 模型

RBAC 模型通过角色关联权限,角色同时又关联用户的授权的方式。一个用户可以拥有若干角色,每一个角色又可以被分配若干权限。

RBAC 数据库设计RBAC 数据库设计

创建不同的角色并为不同的角色分配不同的权限范围(菜单)。

RBAC 模型功能效果RBAC 模型功能效果

Cookie

应用案例:

1、Cookie中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的基本信息给填了。

2、HTTP 协议是无状态的。使用Cookie保存Session或者Token,向后端发送请求的时候带上Cookie,后端获取Session或者Token记录用户当前的状态。

3、Cookie可以用来记录和分析用户行为,将这些信息存放在Cookie服务器获取你在某个页面的停留状态或者看了哪些商品。

浏览器cookies信息浏览器cookies信息

SpringBoot代码实现:

1、设置Cookie返回给客户端

代码语言:javascript复制
@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 的值

代码语言:javascript复制
@GetMapping("/")
public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) {
    return "Hey! My username is "   username;
}

3、读取所有的Cookie

代码语言:javascript复制
@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";
}

Session

应用案例:

1、HTTP 协议是无状态的。当你要添加商品到购物车时,系统不知道是哪个用户操作的。服务端给特定的用户创建特定的Session之后就可以标识这个用户并且跟踪这个用户,服务器记录用户的状态。

单服务器节点Session-Cookie 方案进行身份验证

应用案例:

1、用户成功登陆系统(服务器生成Session一般会选择存放在 Redis数据库),然后返回给客户端具有 SessionID 的 Cookie。

2、当用户向后端发起请求的时候会把SessionID带上,这样后端就知道你的身份状态。

功能步骤:

1、用户向服务器发送用户名、密码、验证码用于登陆系统。

2、服务器验证通过后,服务器为用户创建一个Session,并将Session信息使用Redis存储起来。

3、服务器向用户返回一个 SessionID,写入用户的 Cookie

4、当用户保持登录状态时,Cookie 将与每个后续请求一起被发送出去。

5、服务器可以将存储在 Cookie 上的 SessionID 与存储在内存中或者数据库中的 Session 信息进行比较,以验证用户的状态。

Session-Cookie存在的问题:

1、依赖Session的关键业务一定要确保客户端开启了Cookie。

2、注意 Session 的过期时间(一般是5分钟,退出系统应立刻消除对应的Session记录)。

3、多服务器节点 Session-Cookie。

4、Session信息服务器的可用性。

多服务器节点下 Session-Cookie 方案进行身份验证

应用案例:

部署了两份相同的服务 A,B,用户第一次登陆的时候 ,Nginx 通过负载均衡机制将用户请求转发到 A 服务器,此时用户的 Session信息保存在 A 服务器。结果,用户第二次访问的时候 Nginx 将请求路由到 B 服务器,由于 B 服务器没有保存用户的 Session信息,导致用户需要重新进行登陆。

最佳解决方案:

单独使用所有服务器都能访问的数据节点(Redis缓存)来存放Session信息。为了保证高可用,数据节点尽量避免是单节点。

如何处理客户端禁用Cookie问题(移动端)

SessionID加密之后放在请求的url里面再传入后端与存储在内存中或者数据库中的 Session 信息进行比较

,但是安全性下降,不法分子拿到Session后进行Session分析或者是模拟请求。

Session-Cookie 方案进行身份验证的跨站请求伪造(CSRF)问题

应用案例:

进行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

Token 不需要自己存放Session信息就能实现身份验证的方式,服务器端就不需要保存Session数据了,只用在客户端保存服务端返回给用户的Token

为什么使用Token

1、Session信息需要保存一份在服务器端需要我们保证保存Session信息服务器的可用性。

2、不适合移动端(依赖Cookie)。

JSON Web Token (JWT)组成部分

  1. Header : 描述 JWT 的元数据,定义了生成签名的算法以及 Token 的类型。
  2. Payload : 用来存放实际需要传递的数据。
  3. Signature(签名) :服务器通过PayloadHeader和一个密钥(secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。

Token方案进行身份验证

应用案例:

基于 Token 进行身份验证的的应用程序中,服务器通过PayloadHeader和一个密钥(secret)创建令牌(Token)并将Token发送给客户端。客户端将Token保存在 Cookie 或者 localStorage 里面。以后客户端发出的所有请求都会携带这个令牌。可以把它放在 Cookie 里面自动发送,但是这样不能跨域。更好的做法是放在 HTTP Header 的 Authorization 字段中:Authorization: Bearer Token

功能步骤:

  1. 用户向服务器发送用户名、密码和验证码用于登陆系统。
  2. 身份验证服务响应并返回了签名的 JWT(上面包含了用户身份的内容)。
  3. 用户以后每次向后端发请求都在Header中带上JWT。
  4. 用户检查JWT并获取用户身份信息。

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字段解决跨域问题。

Token认证问题及最佳实践

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

SSO(Single Sign On) 单点登录,用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。

应用案例:

登陆了京东金融之后,同时也成功登陆京东的京东超市、京东国际、京东生鲜等子系统。

单点登录认证优势:

1、用户角度:用户能够做到一次登录多次使用,无需记录多套用户名和密码。

2、系统管理员角度:管理员只需维护好一个统一的账号中心。

3、新系统开发角度:新系统开发时只需直接对接统一的账号中心。

功能模块:

功能模块

说明

系统站点

需要登录的站点

SSO站点-登录

提供登录的页面

SSO站点-登出

提供注销登录的入口

SSO服务-登录

提供登录服务

SSO服务-登录状态

提供登录状态校验/登录信息查询的服务

SSO服务-登出

提供用户注销登录的服务

数据库

存储用户账户信息

缓存

Redis存储用户的登录状态信息

用户登录状态的存储与校验:用户登录成功之后,生成AuthToken交给客户端保存。如果是浏览器,就保存在Cookie中。如果是手机App就保存在App本地缓存中。

对象

说明

AuthToken

直接使用UUID/GUID,如果有验证AuthToken合法性需求,可以将UserName 时间戳加密生成,服务端解密之后验证合法性。

登录信息

通常是将UserID,UserName缓存起来。

SSO服务-登录SSO服务-登录

用户登录/登录校验:

SSO服务-登录状态SSO服务-登录状态

用户登出:

SSO服务-登出SSO服务-登出

跨域登录、登出:客户端存储AuthToken,服务器端通过Redis存储登录信息。由于客户端是将AuthToken存储在Cookie中的,但是Cookie是不能跨域的。

解决Cookie不能跨域的核心思路:

登录完成之后通过回调的方式,将AuthToken传递给主域名之外的站点,该站点自行将AuthToken保存在当前域下的Cookie中。

登出完成之后通过回调的方式,调用非主域名站点的登出页面,完成设置Cookie中的AuthToken过期的操作。

跨域登录(主域名已登录)跨域登录(主域名已登录)
跨域登录(主域名未登录)跨域登录(主域名未登录)
跨域登出跨域登出

OAuth 2.0

OAuth 是行业的标准授权协议,用来授权第三方应用获取有限的权限。为第三方应用颁发一个有时效性的令牌 Token,使得第三方应用能够通过该令牌获取相关的资源。

应用案例:

应用网站接入了第三方登录的一般使用 OAuth 2.0 协议。例如:微信支付、支付宝支付。

OAuth 2.0 第三方认证OAuth 2.0 第三方认证

0 人点赞