八幅漫画理解使用JSON Web Token设计单点登录系统
几种常用的认证机制
-
OAuth
OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。
OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容
[图片上传失败...(image-c7855b-1591695124846)]
这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用;
Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效;
-
Cookie Auth
Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效;
-
Token Auth
[图片上传失败...(image-664cb0-1591695124847)]
Token机制相对于Cookie机制又有什么好处呢?
-
支持跨域访问:
Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输. -
无状态(也称:服务端可扩展行):
Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息. -
更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可.
-
去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可.
-
更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。
-
CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。
-
性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多.
-
不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理.
-
基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft).
maven 依赖
<!--JWT-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.9.4</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
实例
package com.wanggs.cons;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.wanggs.domain.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import org.joda.time.DateTime;
public class JWT {
private static String JWT_KEY = "lOi98Jhy6dt";//PropertyUtil.getProperty("jwt.key","lOi98Jhy6dt");
private static Key key = null;
// token过期时间天数
private static int EXP_DAYS = 30;//Integer.valueOf(PropertyUtil.getProperty("jwt.expDays", "30"));
static {
SecretKeyFactory keyFactory;
try {
keyFactory = SecretKeyFactory.getInstance("PBEWithMD5AndDES");
KeySpec keySpec = new PBEKeySpec(JWT_KEY.toCharArray());
key = keyFactory.generateSecret(keySpec);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
e.printStackTrace();
}
}
public static String create(User user) {
String json = JSON.toJSONString(user);
return Jwts.builder().setSubject(user.getId().toString()).setClaims(JSON.parseObject(json))
.setExpiration(new DateTime().plusDays(EXP_DAYS).toDate()).signWith(io.jsonwebtoken.SignatureAlgorithm.HS512, key).compact();
}
public static User parse(String compactJws) throws NoAuthorization {
if (compactJws == null) {
throw new NoAuthorization();
}
try {
Jws<Claims> claims = Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
// 走到这里说明token验证通过
// 接下来获取信息
Claims body = claims.getBody();
JSONObject jsonObject = new JSONObject(body);
return JSON.toJavaObject(jsonObject, User.class);
} catch (Exception e) {
throw new NoAuthorization();
}
}
public static boolean checkToken(HttpServletRequest request) {
String token = getToken(request);
return checkToken(token);
}
public static boolean checkToken(String compactJws) {
try {
//LOG.debug("检查token:{}", compactJws);
Jwts.parser().setSigningKey(key).parseClaimsJws(compactJws);
return true;
} catch (Exception e) {
return false;
}
}
public static User parse(HttpServletRequest request) throws NoAuthorization {
String token = getToken(request);
return parse(token);
}
public static String getToken(HttpServletRequest request) {
String jwt = request.getHeader("Authorization");
if (jwt == null) {
return null;
}
jwt = jwt.replace("Bearer", "").trim();
return jwt;
}
public static void main(String[] args) {
User user = new User();
user.setId(1L);
user.setOpenId("aaaaa");
user.setNickName("bbbbb");
String jwt = create(user);
System.out.println("jwt:" + jwt);
try {
Thread.sleep(1000);
System.out.println("checkToken:" + checkToken(jwt));
User user2 = parse(jwt);
System.out.println(JSON.toJSONString(user2));
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 输出:
* jwt:eyJhbGciOiJIUzUxMiJ9.eyJuaWNrTmFtZSI6ImJiYmJiIiwib3BlbklkIjoiYWFhYWEiLCJpZCI6MSwiZXhwIjoxNTA3Mjc0OTUzfQ.CvdniqQ29gVokSmh8kj8qrlbgea5Ij49aI0y6Zx0YnjP0djodI7c1SwAYyAyrcMzk5BIau3Z8rMdb3m8z-7Nbg
* checkToken:true
{"id":1,"nickName":"bbbbb","openId":"aaaaa"}
*
*/
}
控制器
@PostMapping("/hello")
@ResponseBody
public String hello(HttpServletRequest request){
User user = UserContext.getLoginUser(request);
System.out.println(user);
return "success";
}
请求头的键值对
Content-Type application/json
Authorization Bearer ....
网友评论