OAuth2.0 第三方登录

对于用户相关的OpenAPI(例如获取用户信息,动态同步,照片,日志,分享等),为了保护用户数据的安全和隐私,第三方网站访问用户数据前都需要显式的向用户征求授权。

  • 以微博登录为例:

登录 - 图1

  • 使用code换取accessToken,一个code只能换取一次
  • 同一个用户的accessToken一段时间内是不会变化的,可以通过accessToken多次获取客户的信息

1、引导需要授权的用户到如下地址:

  1. https://api.weibo.com/oauth2/authorize?client_id=YOUR_CLIENT_ID&response_type=code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI

2、如果用户同意授权,页面跳转至 YOUR_REGISTERED_REDIRECT_URI/?code=CODE
3、访问如下地址换取Access Token
为避免暴露CODE以及用户的密码,在后台服务中获取到CODE后直接由后台发起HTTP请求换取Token

https://api.weibo.com/oauth2/access_token?client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&grant_type=authorization_code&redirect_uri=YOUR_REGISTERED_REDIRECT_URI&code=CODE

其中client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET可以使用basic方式加入header中,返回JSON

{
  "access_token": "SlAV32hkKG",
  "remind_in": 3600,
  "expires_in": 3600
}
  • 换取Token后往往需要在数据库中注册新用户

4、使用获得的Access Token调用API

分布式下session共享问题

  1. 同一个服务可能部署了多个服务器,跨服务器访问导致找不到session
  2. 访问不同的服务,不同服务间session不能共享
  • 解决方案1-session复制

优点:web-server(Tomcat)原生支持,只需要修改配置文件
缺点:
session同步需要数据传输,占用大量网络带宽,降低了服务器群的业务处理能力;
任意一台web-server保存的数据都是所有web-server的session总和,受到内存限制无法水平扩展更多的web-server;
大型分布式集群情况下,由于所有web-server都全量保存数据,所以此方案不可取。

  • 解决方案2-一致性hash负载均衡

优点:
只需要改nginx配置,不需要修改应用代码
负载均衡,只要hash属性的值分布是均匀的,多台 web-server的负载是均衡的
可以支持web-server水平扩展(session同步法是不行的,受内存限制)
缺点:
session还是存在web-server中的,所以web-server重启可能导致部分session丢失,影响业务,如部分用户需要重新登录
如果web-server水平扩展,rehash后session重新分布, 也会有一部分用户路由不到正确的session
但是以上缺点问题也不是很大,因为session本来都是有有效期的。所以这两种反向代理的方式可以使用

  • 解决方案3-统一存储

优点:
没有安全隐患
可以水平扩展,数据库/缓存水平切分即可
web-server重启或者扩容都不会有session丢失
不足:
增加了一次网络调用,并且需要修改应用代码;如将所有的getSession方法替换为从Redis查数据的方式。redis获取数据比内存慢很多
上面缺点可以用SpringSession完美解决

  • 不同服务间的子域session共享

jsessionid这个cookie默认的作用范围是当前域名内,当部署不同域名的服务时,可以手动指定session域名,扩大其作用范围,达到子域session共享的目的
image.png
如上图,将jessionid的domain设置为.gulimall.com之后,其下的所有子域(如会员服务、订单服务等)都可以通过该cookie获取session

可以通过整合spring-session解决这一系列问题

spring-session

1、添加依赖

<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

2、配置session缓存方式

spring.session.store-type=redis

3、主配置类中开启spring-session:@EnableRedisHttpSession

  • Spring Boot 在启动时会通过自定义的filter替换之前默认使用的HttpSession,在需要使用session时,直接在controller中注入即可(HttpSession)

  • 默认发放的令牌的作用域为当前域,需要解决子域session共享问题:注册一个自定义的cookie序列化器

  • 使用JSON格式将对象序列化到redis中

    @Configuration
    public class GulimallSessionConfig{
    @Bean
    public CookieSerializer cookieserializer() {
      DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
          // 设置cookie的作用域
      cookieSerializer.setDomainName(".gulimall.com");
      return cookieSerializer;
    }
    
    @Bean
    public RedisSerializer<Object>     springSessionDefaultRedisSerializer() {
          // 返回一个JSON格式的Redis序列化器
      return new XXXJsonRedisSerializer();
    } 
    }
    
  • 原理

image.png

单点登录SSO

single sign on 一次登录处处可用
在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。如登录gulimall.com后,访问gulifunding.com、gulixueyuan.com等应用时可以不用再进行登录验证

  • 流程

1、访问应用1时,未携带用户信息,需要进行登录操作。通过请求头或者cookie携带网站地址信息,便于登录服务器完成操作后进行跳转
2、登录服务器根据用户名和密码进行登录验证,将查询到的用户信息作为value放入session中(Redis提供session服务);为此次登录操作生成一个UUID(相当于一个token令牌),作为key放入Redis;并且将UUID作为cookie返回给浏览器。
3、登录成功,根据携带的地址信息进行跳转。需要使用用户信息时,浏览器根据返回的UUID(cookie)获取用户信息
4、访问应用2时,判断cookie中是否存在UUID,若存在,说明已经在别的站点进行过登录

登录 - 图4

  • 单点登录是一种有状态登录

为了保证客户端 cookie 的安全性,服务端需要记录每次会话的客户端信息,从而识别客户端身份,根据用户身份进行请求的处理,典型的设计如 tomcat 中的 session。
例如登录:用户登录后,我们把登录者的信息保存在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session。然后下次请求,用户携带 cookie 值来,我们就能识别到对应 session, 从而找到用户的信息。

  • 缺点
    - 服务端保存大量数据,增加服务端压力
    - 服务端保存用户状态,无法进行水平扩展
    - 客户端请求依赖服务端,多次请求必须访问同一台服务器

JWT

JWT,全称是 Json Web Token, 是 JSON 风格轻量级的授权和身份认证规范,可实现无状态、 分布式的 Web 应用授权
JWT其实就是一个很长的字符串,字符之间通过”.”分隔符分为三个子串,各字串之间没有换行符。每一个子串表示了一个功能块,总共有三个部分:JWT头(header)、有效载荷(payload)、签名(signature)

点击查看【processon】

基于Shiro框架的认证鉴权

image.png

  • Shiro通过Realm处理认证和鉴权流程,并且通过SessionManager会话管理器将用户安全数据存入共享的缓存中(由Redis实现)
  • 用户登录时可以将sessionId保存在请求头中,通过携带sessionId进行权限认证
  • 类似于cookie-session机制,也是一种有状态的登录方式,但支持分布式架构,同时不依赖cookie(sessionId存储在请求头中)