Bootstrap

JWT及鉴权

系列文章目录


前言

JWT的学习
鉴权的学习(设计到接口权限设计)

一、JWT原理解析

1.1 JWT是什么?

JSON Web 令牌 (JWT) 是一个开放标准‎,它定义了一种紧凑且自包含的方式,用于将信息作为 JSON 对象安全地在各方之间传输信息。

  • 简单来说就是客户端和服务端安全传输信息的一个标准

1.2 JWT的结构组成

JWT由三部分组成,中间使用 . 连接,三部分都是由base64编码的,由此来保证url安全的传输。

  • 第一部分:heaer():由两部分信息组成:
    1、声明类型jwt
    2、声明加密的算法
    再使用base64URL编码:
    例如:
{
    'typ':'jwt',
    'alg':'SHA256'
}

使用base64URL编码后:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

  • 第二部分:payload(载荷):存储非敏感信息,比如用户id和更新的时间
    再使用base64URL编码:
    例如:
{
	'id':'10',
	'exp':'2301597433'
}

base64URL编码之后为:ewoJJ2lkJzonMTAnLAoJJ2V4cCc6JzIzMDE1OTc0MzMnCn0=

  • 第三部分:signature :存储最重要的信息
    它的组成原理是:1、将header和payload分别base64URL编码之后组合到一起(通过"."连接);2、添加一个只有服务器知道的签名字符串;3、再使用header中的签名算法SHA256加密步骤1、2。可以看成下面的公式:
signature = SHA256(base64encode(header) + '.' + 
base64encode(payload), 'SEVER_SECRET_KEY')

加密后变成为:05dd35b4d20c95430cd1b63406f861de7e4c1476f9dbffa25f30fe08baf8f530

  • 最终呈现出JWT为:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.ewoJJ2lkJzonMTAnLAoJJ2V4cCc6JzIzMDE1OTc0MzMnCn0=.05dd35b4d20c95430cd1b63406f861de7e4c1476f9dbffa25f30fe08baf8f530

1.3 JWT的优缺点

1.3.1 优点

  • 相比于session,它无需保存在服务器,不占服务器内存开销;

  • 无状态、可扩展性性强:
    应用程序分布式部署的情况下,客户端的一个令牌可以访问所有的服务器的同时,也避免了服务器之间做session id的多机数据共享。
    意思就是
    比如有3台机器(A、B、C)组成服务器集群,若session存在机器A上,session只能保存在其中一台服务器,此时你便不能访问机器B、C,因为B、C上没有存放该Session,而使用token就能够验证用户请求合法性,并且我再加几台机器也没事,所以可拓展性好就是这个意思。

  • 由上可知可以支持跨域访问;

  • 性能:
    JWT中信息的存储,可以有效的减少服务器查询数据库的次数。

1.3.2 缺点

  • 安全性:
    从以上的描述中我们可以看到,payload中的信息只是进行了base64URL编码,并没有加密,所以不能存放敏感信息;同时,JWT如果泄露会被人冒用身份,为防止盗用,JWT应尽量使用https协议传输。
  • 性能:
    JWT为了做到安全性,导致其本身很长,即http请求开销增加。

二、JWT的使用

2.1 导入依赖

		<dependency>
		    <groupId>com.auth0</groupId>
		    <artifactId>java-jwt</artifactId>
		    <version>3.10.3</version>
		</dependency>

2.2 生成token

public class JWTExample {

    static String signature = "secritKey";

    public static void main(String[] args) {
        //使用JWT自带的构造器构造一个jwt
        JwtBuilder builder = Jwts.builder();
        String token = builder.
                //封装header属性(声明类型JWT、声明加密的算法)
                    setHeaderParam("typ","JWT").
                    setHeaderParam("alg", "HS256")
                //封装payload里的信息 使用claim方法(封装一些不敏感的信息)
                    .claim("username", "zhangsan")
                    .claim("name", "张三")
                    .claim("age", 18)
                    .claim("sex", "女")
                //在payLoad中设置一个超时时间  秒   分 时
                    .setExpiration(new Date(System.currentTimeMillis()+Long.valueOf(1000 * 60 * 60 * 1)))
                    //一个识别id(不重要)
                    .setId("1212121212")
                //构造signature部分
                    .signWith(SignatureAlgorithm.HS256, signature)
                //构造我们的签名 调用compact方法
                    .compact();
        System.out.println(token);

	}
}

2.2 解析token

//解析token
public Jws<Claims> parseJWT(String token,String signature){
  		//解密
        JwtParser parser = Jwts.parser();
        Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(token);
        Claims body = claimsJws.getBody();
        //获取name
        System.out.println(body.get("name"));
        //获取性别
        System.out.println(body.get("sex"));
        //获取用户名
        System.out.println(body.get("username"));
        //获取姓名
        System.out.println(body.get("name"));
        //获取id
        System.out.println(body.getId());
        //获取有效期-截止时间
        System.out.println(body.getSubject());
        System.out.println(body.getExpiration());
        return claimsJws;
}

2.3 使用场景

验证登录状态和接口权限

2.3.1 注意

JWT是 设置了过期时间,若客户端在访问是JWT过期了,该怎么办?

  • 用户重新登录(由前端提醒用户重新登录,或者强制下线,让用户自己重新登录)

  • 续命:

    JWT一般存储在redis中,我们可以判断用户使用系统时判断JWT过期时间,然后延长有效期时间

三、 鉴权(接口权限)

3.1 什么是鉴权?

  • 鉴权(authentication)是指验证用户是否拥有访问系统的权利。传统的鉴权是通过密码来验证的。这种方式的前提是,每个获得密码的用户都已经被授权。在建立用户时,就为此用户分配一个密码,用户的密码可以由管理员指定,也可以由用户自行申请。这种方式的弱点十分明显:一旦密码被偷或用户遗失密码,情况就会十分麻烦,需要管理员对用户密码进行重新修改,而修改密码之前还要人工验证用户的合法身份。
  • 为了克服这种鉴权方式的缺点,需要一个更加可靠的鉴权方式。主流鉴权方式是利用认证授权来验证数字签名的正确与否。
  • 逻辑上,授权发生在鉴权之后,而实际上,这两者常常是一个过程。

3.2 鉴权分类

  • 用户鉴权,网络对用户进行鉴权,防止非法用户占用网络资源。

  • 网络鉴权,用户对网络进行鉴权,防止用户接入了非法的网络,被骗取关键信息。

这种双向的认证机制,就是AKA(Authentication and Key Agreement,鉴权和密钥协商)鉴权。

除了AKA鉴权,也可以使用其它鉴权方式。在IMS AKA鉴权广泛实施之前,或在特定的条件下(例如通过固定网络ADSL连接方式接入IMS),可以使用HTTP摘要鉴权等其他鉴权方式。

3G UMTS(Universal Mobile Telecommunication System,通用移动通讯系统)、EPS(Evolved Packet System,演进的分组系统)、IMS(IP Multimedia Subsystem,IP多媒体子系统)网络都采用了AKA双向鉴权机制,鉴权原理也大致相同。而2G网络,只有用户鉴权,无网络鉴权。

3.3 鉴权时机

移动网络对鉴权时机的要求为:

  • 2G、3G网络中,鉴权发生在开机、呼叫、位置更新以及在补充业务的激活、去活、登记或删除操作之前。
  • 2G网络中,运营商都是启用的“按比例鉴权”方案。
  • 3G网络中,用户首次接入网络必须鉴权,此后启用“按比例鉴权”方案。
  • IMS网络中,网络可以通过注册或重注册过程,在任何时候对用户进行鉴权。

3.4 鉴权方式

常用的鉴权有四种:

1、HTTP Basic Authentication

2、session-cookie

3、Token 验证

4、OAuth(开放授权)

3.4.1 HTTP Basic Authentication

这种授权方式是浏览器遵守http协议实现的基本授权方式,HTTP协议进行通信的过程中,HTTP协议定义了基本认证认证允许HTTP服务器对客户端进行用户身份证的方法。

认证过程:

1. 客户端向服务器请求数据,请求的内容可能是一个网页或者是一个ajax异步请求,此时,假设客户端尚未被验证,则客户端提供如下请求至服务器:

		Get /index.html HTTP/1.0
		Host:www.google.com

2. 服务器向客户端发送验证请求代码401,(WWW-Authenticate: Basic realm=”google.com”这句话是关键,如果没有客户端不会弹出用户名和密码输入界面)服务器返回的数据大抵如下:

		HTTP/1.0 401 Unauthorised
		Server: SokEvo/1.0
		WWW-Authenticate: Basic realm=”google.com”
		Content-Type: text/html
		Content-Length: xxx

3. 当符合http1.0或1.1规范的客户端(如IE,FIREFOX)收到401返回值时,将自动弹出一个登录窗口,要求用户输入用户名和密码。

4. 用户输入用户名和密码后,将用户名及密码以BASE64加密方式加密,并将密文放入前一条请求信息中,则客户端发送的第一条请求信息则变成如下内容:

		Get /index.html HTTP/1.0
		Host:www.google.com
		Authorization: Basic d2FuZzp3YW5n

注:d2FuZzp3YW5n表示加密后的用户名及密码(用户名:密码 然后通过base64加密,加密过程是浏览器默认的行为,不需要我们人为加密,我们只需要输入用户名密码即可)

5. 服务器收到上述请求信息后,将Authorization字段后的用户信息取出、解密,将解密后的用户名及密码与用户数据库进行比较验证,如用户名及密码正确,服务器则根据请求,将所请求资源发送给客户端。

效果:
客户端未未认证的时候,会弹出用户名密码输入框,这个时候请求时属于pending状态,这个时候其实服务当用户输入用户名密码的时候客户端会再次发送带Authentication头的请求。

3.4.2 session-cookie

HTTP Cookie(也叫Web Cookie或浏览器Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie使基于无状态的HTTP协议记录稳定的状态信息成为了可能。

(一)Cookie主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

(二) 认证过程
1、服务器在接受客户端首次访问时在服务器端创建seesion,然后保存seesion(我们可以将seesion保存在内存中,也可以保存在redis中,推荐使用后者),然后给这个session生成一个唯一的标识字符串,然后在响应头中种下这个唯一标识字符串。

2、签名。这一步只是对sid进行加密处理,服务端会根据这个secret密钥进行解密。(非必需步骤)

3、浏览器中收到请求响应的时候会解析响应头,然后将sid保存在本地cookie中,浏览器在下次http请求de 请求头中会带上该域名下的cookie信息。

4、服务器在接受客户端请求时会去解析请求头cookie中的sid,然后根据这个sid去找服务器端保存的该客户端的session,然后判断该请求是否合法。

5、一旦用户登出,服务端和客户端同时销毁该会话在后续请求中,服务器会根据数据库验证会话id,如果验证通过,则继续处理;

3.4.3 Token 验证

(一) 认证过程

  • 用户输入登陆凭据;
  • 服务器验证凭据是否正确,然后返回一个经过签名的token;
  • 客户端负责存储token,可以存在localstorage,或者cookie中
  • 对服务器的请求带上这个token;
  • 服务器对JWT进行解码,如果token有效,则处理该请求;
  • 一旦用户登出,客户端销毁token。

(二) cookie与taken性能对比

  • sessionid 他只是一个唯一标识的字符串,服务端是根据这个字符串,来查询在服务器端保持的seesion,这里面才保存着用户的登陆状态。但是token本身就是一种登陆成功凭证,他是在登陆成功后根据某种规则生成的一种信息凭证,他里面本身就保存着用户的登陆状态。服务器端只需要根据定义的规则校验这个token是否合法就行。
  • session-cookie是需要cookie配合的,居然要cookie,那么在http代理客户端的选择上就是只有浏览器了,因为只有浏览器才会去解析请求响应头里面的cookie,然后每次请求再默认带上该域名下的cookie。但是我们知道http代理客户端不只有浏览器,还有原生APP等等,这个时候cookie是不起作用的,或者浏览器端是可以禁止cookie的,但是token就不一样,他是登陆请求在登陆成功后再请求响应体中返回的信息,客户端在收到响应的时候,可以把他存在本地的cookie,storage,或者内存中,然后再下一次请求的请求头重带上这个token就行了。简单点来说cookie-session机制他限制了客户端的类型,而token验证机制丰富了客户端类型。
  • 时效性。session-cookie的sessionid实在登陆的时候生成的而且在登出事时一直不变的,在一定程度上安全就会低,而token是可以在一段时间内动态改变的。
  • 可扩展性。token验证本身是比较灵活的,一是token的解决方案有许多,常用的是JWT,二来我们可以基于token验证机制,专门做一个鉴权服务,用它向多个服务的请求进行统一鉴权。

3.4.4 OAuth(开放授权)

OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是OAUTH的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此OAUTH是安全的。同时,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。

我们常见的提供OAuth认证服务的厂商有支付宝,QQ,微信。

OAuth协议又有1.0和2.0两个版本。相比较1.0版,2.0版整个授权验证流程更简单更安全,也是目前最主要的用户身份验证和授权方式。

典型案例

果一个用户拥有两项服务:一项服务是图片在线存储服务A,另一个是图片在线打印服务B。由于服务A与服务B是由两家不同的服务提供商提供的,所以用户在这两家服务提供商的网站上各自注册了用户,假设这两个用户名各不相同,密码也各不相同。

当用户要使用服务B打印存储在服务A上的图片时,用户该如何处理?

  • 方法一:
    用户可能先将待打印的图片从服务A上下载下来并上传到服务B上打印,这种方式安全但处理比较繁琐,效率低下;

  • 方法二:
    用户将在服务A上注册的用户名与密码提供给服务B,服务B使用用户的帐号再去服务A处下载待打印的图片,这种方式效率是提高了,但是安全性大大降低了,服务B可以使用用户的用户名与密码去服务A上查看甚至篡改用户的资源。

  • 方法三:
    当服务B(打印服务)要访问用户的服务A(图片服务)时,通过OAUTH机制,服务B向服务A请求未经用户授权的RequestToken后,服务A将引导用户在服务A的网站上登录,并询问用户是否将图片服务授权给服务B。用户同意后,服务B就可以访问用户在服务A上的图片服务。整个过程服务B没有触及到用户在服务A的帐号信息。

OAuth相关术语

在认证和授权的过程中涉及的三方包括:

服务提供方(ServiceProvider),用户使用服务提供方来存储受保护的资源,如照片,视频,联系人列表。

用户(User),存放在服务提供方的受保护的资源的拥有者

- 服务提供方(ServiceProvider),用户使用服务提供方来存储受保护的资源,如照片,视频,联系人列表。

- 用户(User),存放在服务提供方的受保护的资源的拥有者

客户端(Consumer),要访问服务提供方资源的第三方应用,通常是网站,如提供照片打印服务的网站也可以是桌面或移动应用程序。在认证过程之前,客户端要向服务提供者申请客户端标识。

OAuth相关的三个URL:

- RequestToken URL:获取未授权的RequestToken服务地址;

- UserAuthorization URL:获取用户授权的RequestToken服务地址;

- AccessToken URL:用授权的RequestToken换取AccessToken的服务地址。

OAuth认证和授权过程

- 1、客户端(第三方软件)向OAUTH服务提供商请求未授权的RequestToken。即RequestToken URL发起请求;
- 
- 2、OAUTH服务提供商同意使用者的请求,并向其颁发未经用户授权的oauth_token与对应的oauth_token_secret,并返回给使用者;
- 
- 3、使用者向OAUTH服务提供商请求用户授权的RequestToken。即向UserAuthorization URL发起请求并在请求中携带上一步服务提供商颁发的未授权的token与其密钥;
- 
- 4、OAUTH服务提供商通过网页要求用户登录并引导用户完成授权;
- 
- 5RequestToken授权后,使用者将向AccessToken URL发起请求,将上步授权RequestToken换取成AccessToken。请求的参数见上图,这个比第一步多了一个参数就是RequestToken- 
- 6、OAUTH服务提供商同意使用者的请求,并向其颁发AccessToken与对应的密钥,并返回给使用者;
- 
- 7、使用者以后就可以使用上步返回的AccessToken访问用户授权的资源。

简单整理就三个步骤

1、获取未授权的RequestToken
2、获取用户授权的RequestToken
3、用授权的RequestToken换取AccessToken

四、鉴权的应用

TODO

;