JWT学习(二):JWT在分布式SSO中的应用实例


上一篇文章讲解了JWT的基本简介,这一篇文章我就来实战一下。介绍一下在分布式单点登录中的使用方法:


首先来看一下Token实体类,

public class Token implements Serializable{
	private static final long serialVersionUID = -5391652691006115018L;
	
	/** 认证头 **/
	private Head head;
	/** 认证信息有效载荷 **/
	private Playload playload;
	
	/** 第一部分:base64head头 **/
	private String base64Head;
	/** 第二部分:base64playload荷载 **/
	private String base64PlayLoad;
	/** 第三部分:签证信息 **/
	private String signature;
	/** 最终token字符串 **/
	private String tokenStr;
	
	/**
	 * Token头
	 *
	 *
	 */
	public static class Head implements Serializable{
		private static final long serialVersionUID = -6516084948347601103L;
		
		/** token类型 **/
		private String typ = "JWT";
		/** token算法 默认:HMAC SHA256**/
		private String alg = "HS256";
		
		public String getTyp() {
			return typ;
		}
		public void setTyp(String typ) {
			this.typ = typ;
		}
		public String getAlg() {
			return alg;
		}
		public void setAlg(String alg) {
			this.alg = alg;
		}
	}
	
	/**
	 * Token有效载荷
	 * 
	 *
	 */
	public static class Playload implements Serializable {
		private static final long serialVersionUID = 3981890375700111920L;
		
		/** 该token签发者 **/
		private String iss;
		/** 该token的所有人,可以存放用户名 **/
		private String sub;
		/** 接收token的一方 **/
		private String aud;
		/** token的过期时间(时间戳),必须要大于签发时间;大于等于该时间需要刷新token **/
		private long exp;
		/** token生效的开始时间(时间戳),意味着在这个时间之前验证token是会失败的,默认生成token后立即生效 **/
		private long nbf;
		/** token的签发时间 时间戳**/
		private long iat;
		/** token的唯一身份标识,主要用来作为一次性token,从而回避重放攻击 **/
		private String jti;
		/** token验证宽限时间(时间戳) 超过宽限时间需要重新登录,  
		 * 即该token的真正存活时间,宽限时间的加入是为了解决并发token刷新后新token失效问题
		 * **/
		private long gra;
		/** token类型:  后台登录用户,互联网用户,第三方机构 **/
		private String typ;
		
		public String getIss() {
			return iss;
		}
		public void setIss(String iss) {
			this.iss = iss;
		}
		public String getSub() {
			return sub;
		}
		public void setSub(String sub) {
			this.sub = sub;
		}
		public String getAud() {
			return aud;
		}
		public void setAud(String aud) {
			this.aud = aud;
		}
		public long getExp() {
			return exp;
		}
		public void setExp(long exp) {
			this.exp = exp;
		}
		public long getNbf() {
			return nbf;
		}
		public void setNbf(long nbf) {
			this.nbf = nbf;
		}
		public long getIat() {
			return iat;
		}
		public void setIat(long iat) {
			this.iat = iat;
		}
		public String getJti() {
			return jti;
		}
		public void setJti(String jti) {
			this.jti = jti;
		}
		public long getGra() {
			return gra;
		}
		public void setGra(long gra) {
			this.gra = gra;
		}
		public String getTyp() {
			return typ;
		}
		public void setTyp(String typ) {
			this.typ = typ;
		}

		
	}

	public Head getHead() {
		return head;
	}
	public void setHead(Head head) {
		this.head = head;
	}
	public Playload getPlayload() {
		return playload;
	}
	public void setPlayload(Playload playload) {
		this.playload = playload;
	}
	public String getSignature() {
		return signature;
	}
	public void setSignature(String signature) {
		this.signature = signature;
	}
	public String getBase64Head() {
		return base64Head;
	}
	public void setBase64Head(String base64Head) {
		this.base64Head = base64Head;
	}
	public String getBase64PlayLoad() {
		return base64PlayLoad;
	}
	public void setBase64PlayLoad(String base64PlayLoad) {
		this.base64PlayLoad = base64PlayLoad;
	}
	public String getTokenStr() {
		return tokenStr;
	}
	public void setTokenStr(String tokenStr) {
		this.tokenStr = tokenStr;
	}
	
}

可以看到实体类里面包含了head payload signature这三部分。

然后用户登录成功之后创建token的代码如下:

public static Token createToken(String secret,String tokenId,TokenType tokenType,String userName) {
		try {
			Token token = new Token();
			//创建头
			Token.Head head = new Token.Head();
			head.setAlg("HS256");
			head.setTyp("JWT");
			
			//创建载荷
			//签发时间
			long iat = System.currentTimeMillis();
			//过期时间 20 分钟后过期
			long exp = iat + AuthConstants.TOKEN_EXP_TIME ;
			//最后存活时间
			long gra = iat + AuthConstants.TOKEN_GRA_TIME ;
			Token.Playload playload = new Token.Playload();
			playload.setAud("CLIENT"); //接收token的一方
			playload.setIat(iat); //签发时间
			playload.setExp(exp);
			playload.setGra(gra);
			playload.setIss("AUTH_CENTER");//签发者
			playload.setJti(tokenId); //token唯一身份标识
			playload.setNbf(iat);//生效时间,立即生效
			playload.setSub(userName); //token的所属者,可以存放用户名
			playload.setTyp(tokenType.toString().toUpperCase());
			
			//创建token
			String base64Head = Base64Util.encodeStr(JSONUtil.toJson(head) );
			String base64Playload = Base64Util.encodeStr(JSONUtil.toJson(playload) );
			String signature = HmacUtil.encryptHMACSHA256(base64Head+"."+base64Playload, secret); //token签名
			String tokenStr = base64Head+"."+base64Playload+"."+signature; //token字符串
			
			//组装token对象
			token.setHead(head);
			token.setPlayload(playload);
			token.setBase64Head(base64Head);
			token.setBase64PlayLoad(base64Playload);
			token.setSignature(signature);
			token.setTokenStr(tokenStr);
			return token;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("生成token失败:{}",e.getMessage());
		}
		return null;
	}

创建成功之后,要把token放到响应头中,setHeader方法name参数要用Authorization,value值要使用

"Bearer "+token。

然后用户访问需要权限的接口都需要在请求头加上token,因为使用了Spring Cloud微服务架构,因此请求

会统一通过API网关访问,所以需要在网关处验证token的合法性,使用下面这个parseToken的方法:

/**
	 * 验证并解析token
	 * @param token token字符串
	 * @param secret token签名的盐(密钥)
	 * @return 成功返回token ,失败返回ull
	 */
	public static Token parseToken(String token,String secret) {
		try {
			//判断token是否是合法格式的token串
			if(StringUtils.isEmpty(token)) {
				throw new AuthException("token串为空!");
			}
			if(StringUtils.isEmpty(secret)) {
				throw new AuthException("解析token时,token密钥为空!");
			}
			String[] tokens = token.split("\\.");
			if(tokens==null || tokens.length!=3) {
				throw new AuthException("非法格式的token串:"+token);
			}
			
			//token分解
			String base64Head = tokens[0].trim(); //token头
			String base64Playload = tokens[1].trim(); //token载荷
			String signature = tokens[2].trim(); //token签名
			//验证签名是否为合法的
			String signData = base64Head+"."+base64Playload;
			String signaturedStr = HmacUtil.encryptHMACSHA256(signData, secret.trim()); //token签名
			if(!signature.equals(signaturedStr)) {
				throw new AuthException("非法的token:解析token时,token验签失败!");
			}
			
			Token.Head head = JSONUtil.toBean(Base64Util.decodeStr(base64Head), Token.Head.class);
			Token.Playload playLoad = JSONUtil.toBean(Base64Util.decodeStr(base64Playload), Token.Playload.class);
			Token rs = new Token();
			rs.setHead(head);
			rs.setPlayload(playLoad);
			rs.setSignature(signature);
			return rs;
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("解析token失败:{}",e.getMessage());
			return null;
		}
	}

使用JWT进行身份验证的基本方法的实例就到这里,没有接触过JWT的同学可以先看一下JWT

的简介:http://blog.csdn.net/a18716374124/article/details/78474955

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值