Cookie,Session,Token来进行身份认证

1. Cookie,Session,Token 简单介绍


因为,HTTP是无状态的,HTTP访问了服务器,服务器是没有办法知道又是你这个HTTP发送的。所以不能通过HTTP来进行登录后的身份认证的效果。

这个时候就需要Cookie,Session,Token来进行身份认证。

其实,无论Cookie,Session,Token实现身份认真的本质就是存储。

2. Cookie

2.1 Cookie 概述


Cookies的流程:

  • 1.用户发送HTTP请求进行登录。

  • 2.用户登录成功,服务器就会创建一个Cookie,将该Cookie添加到Set-Cookie的一个响应头,并且携带用户名和密码(一般是加密的,也可能不会携带用户名密码)。
    在这里插入图片描述

  • 3.用户每次发送HTTP请求的时候,都会在请求头上面加上Cookie的请求头,该请求头存放着服务器返回的Cookie。
    在这里插入图片描述

  • 4.服务器进行验证。


Cookie的流程图如下:
在这里插入图片描述


因为,Cookie是不安全的,因为我们的密码用户都是存在Cookie中的,就算进行了加密,某种程度上也是暴露的。如下:

  • 例如:我不用钻研你这个加密算法是什么,我只需要拿到你的这个cookie里面加密后的东西,直接用,直接发到你的服务器上面。照样可以登录你的信息。

因此,就有了Session。


总结:

  • Cookie大部分的使用都是作为一个载体来传输数据的(数据载体)。

2.2 Cookie的使用


cookie.setPath("/")方法使用:

  • setPath方法作用就是规定那个路径携带cookie。
  • cookie的路径一般要设置为/,cookie.setPath(“/”)设置了斜杆后,访问所有相关Url路径(例如:http://www.itholmes.com/xxx)下的请求都会携带该cookie,尤其像登录验证。
  • cookie的默认是当前路径,默认规则如下:

在这里插入图片描述
例如:
在这里插入图片描述


cookie.setMaxAge()方法

  • 该方法作用就是设置cookie存活时间。
    • 如果maxAge为负数,则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效,关闭窗口后该Cookie即失效。
    • maxAge为负数的Cookie,为临时性Cookie,不会被持久化,不会被写到Cookie文件中。
    • Cookie信息保存在浏览器内存中,因此关闭浏览器该 Cookie就消失了。Cookie默认的maxAge值为-1。
    • 一般我们删除cookie直接将其设置为0就可以了,0代表立刻失效的意思。

cookie.setDomain()方法:

  • 跨域共享cookie的方法:设置cookie.setDomain(“.itholmes.com”); 这样当前cookie就只会后缀为.itholmes.com的域名才会携带发送。

cookie.setSecure(false)方法:
cookie.setSecure(false) 规定是否是安全协议,也就是https协议。默认是false。

3. Session


Session会话机制是基于Cookie的。

  • 1.用户发送http请求协议,进行登录。
  • 2.登录成功后,服务器创建一个SessionID,Max-age(存活时间)等Session信息,并且对含有SessionID的Cookie进行签名,之后将Cookie放到Set-Cookie的响应头中。
  • 3.浏览器之后每次想服务器发送请求的时候都会携带这个Session信息,放到Cookie中,再将该Cookie存在一个名为Cookie的请求头中,发给服务器。
  • 4.服务器进行验证。
    在这里插入图片描述

但是,随着集群的架构出现,多台服务器之间必须要共享Session会话才行!

因此,就有了将Session会话信息存储到数据库中,一般数据库选择redis非关系型。

但是,如果数据库崩溃了,那么就会彻底影响session了。
在这里插入图片描述


总结:

  • session是存储在服务器端的,用户每次发送请求都携带一个保存session信息的的cookie。

4. 为什么使用JWT?


cookie和session的缺点:

  • session存储在服务端内存中
  • 集群环境中需要额外处理。
  • cookie跨域读写不方便。

而JWT,token的优点就是弥补了上面的缺点。因为,token主要来解决前后端分离的场景。

5. JWT(JSON Web Token)的实现过程


JWT任何语言都可以使用!因为JWT是一种思想。具体的语言实现操作才是JWT实现。

token的英文直译是令牌的意思。

JWT全称叫做JSON Web Token,一般我们叫做token实现登录验证。

  • 1.用户发送请求协议进行登录。
  • 2.之后服务器会生成一个JWT和JWT签名密文,服务器会将JWT发送给浏览器,浏览器可以通过Cookie或者Storage本地存储的形式来存储。
  • 3.浏览器再次发送请求,就会把JWT的信息存到一个名为token的请求头中,发送给服务器。
  • 4.服务器进行验证。
    在这里插入图片描述

JWT需要的salt盐作用就是加强加密的复杂度,这么破解起来就更加麻烦,当然这个“盐”越长越复杂,加密后破解起来就越麻烦。


JWT是由三部分组成的:
在这里插入图片描述
在这里插入图片描述


总结:

  • token是诞生在服务器,但保存在浏览器这边的。可以放到cookie或storage中。持有token就像持有令牌一样可以允许访问服务器。

6. 官方实现JWT的多种方式


JWT官方网站

我们去Libraries中,找到Java相关的JWT。

在这里插入图片描述


这里我们就是使用两种官方的JWT实现:

  • java-jwt
  • jjwt-root
    在这里插入图片描述
    在这里插入图片描述

7. JWT的实现第一种:java-jwt包的使用


第一步:导包,导依赖。

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.18.3</version>
</dependency>

第二步:调用JWT。

  • jwt三部分组成:header.payload.signature。
  • 需要我们配置的也就只有payload,通过withClaim来配置。
import java.util.Calendar;

import org.junit.Test;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.sun.org.apache.bcel.internal.generic.ALOAD;

public class JavaJwtTest {
	
	//hmac256摘要算法,跟md5类似,只是这个hmac256需要指定key,通常这个key叫做salt盐。
	String key = "123456abc";
	
	/*
	 * 测试java-jwt生成token字符串
	 * 		jwt三部分组成:
	 * 			header.payload.signature
	 */
	@Test
	public void testGenerateToken() {
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.SECOND, 60*5);
		
		//claim英文直译:声明意思
		Builder builder = JWT.create()
			//withClaim中设定的就是payload的内容。
			.withClaim("userid", 123)
			.withClaim("userName", "itholmes")
			.withClaim("url", "https//www.itholmes.top")
			//设置过期时间
			.withExpiresAt(calendar.getTime());
		
		String token = builder.sign(Algorithm.HMAC256(key));
		System.out.println(token);
	}
	
	//校验
	@Test
	public void testVerify() {
		String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Iml0aG9sbWVzIiwiZXhwIjoxNjQ2NzUxMTk3LCJ1c2VyaWQiOjEyMywidXJsIjoiaHR0cHMvL3d3dy5pdGhvbG1lcy50b3AifQ.e7y_9iO_jtVXo90qaq5H0aiUmg29I9XeJqAgUCDb1zo";
		JWTVerifier build = JWT.require(Algorithm.HMAC256(key)).build();
		DecodedJWT verify = null;
		try{
			verify = build.verify(token);
			System.out.println(verify);
		}catch(TokenExpiredException e) {
			System.out.println("该token已经过期。");
		}catch (SignatureVerificationException e) {
			System.out.println("签名不一致");
		}catch (AlgorithmMismatchException e) {
			System.out.println("签名算法不匹配");
		}catch (InvalidClaimException e) {
			System.out.println("payload不可用");
		}catch (Exception e) {
			System.out.println("其他错误校验失败");
		}
		
		System.out.println(verify);
		if(verify!=null) {
			//asInt()方法转换类型。
			Integer userid = verify.getClaim("userid").asInt();
			String userName = verify.getClaim("userName").asString();
			String url = verify.getClaim("url").asString();
			System.out.println();
		}
	}
}

8. JWT的实现第二种:jjwt包的使用


第一步:同样导包导依赖。

<dependency>
     <groupId>io.jsonwebtoken</groupId>
	 <artifactId>jjwt</artifactId>
	 <version>0.9.1</version>
</dependency>

第二步:Jwts调用。

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import org.junit.Test;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JJwtTest {
	String key="123435abc";
	
	//通过jjwt生成token字符串
	@Test
	public void testGenerateToken() {
		//过期时间
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.SECOND, 60*5);
		//创建payLoad的私有声明(根据特定的业务需要添加)
		Map<String, Object> claims = new HashMap<String, Object>();
		claims.put("userid",123);
		claims.put("userName","itholmes");
		claims.put("url","https//www.itholmes.top");
		JwtBuilder builder = Jwts.builder()
					//设置payLoad,传入的是map类型
					.setClaims(claims)
					//设置过期时间
					.setExpiration(calendar.getTime())
					//设置签名使用的签名算法和签名使用的秘钥
					.signWith(SignatureAlgorithm.HS256, key);
		String compact = builder.compact();
		System.out.println(compact);
	}
	
	//校验token
	@Test
	public void testVerify() {
		String token="eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6Iml0aG9sbWVzIiwiZXhwIjoxNjQ2NzUyNjcxLCJ1c2VyaWQiOjEyMywidXJsIjoiaHR0cHMvL3d3dy5pdGhvbG1lcy50b3AifQ.6M9yg8iGutEgoYNv97n3GV5_o29iL2YXbrdpME_xtXc";
		Claims claims = Jwts.parser()
				.setSigningKey(key)//设置签名秘钥
				.parseClaimsJws(token).getBody();//设置需要解析的jwt
		Integer userId = claims.get("userid",Integer.class);
		String userName = claims.get("userName",String.class);
		String url = claims.get("url",String.class);
		
		System.out.println();
	}
}

9. 封装JWT的包装类,方便以后使用


package com.itholmes.utils;

import java.util.Calendar;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator.Builder;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.InvalidClaimException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itholmes.bean.User;

public class JwtUtil {
	
	//jwt的key也就是salt盐
	private static final String key = "www.itholmes.top";
	
	//生成token
	public static String generateToken(User user) throws JsonProcessingException {
		
		//使用jackson来生成json字符串方便存取
		ObjectMapper jackson = new ObjectMapper();
		String user_string = jackson.writeValueAsString(user);
		
		//通过calendar设置过期时间
		Calendar calendar = Calendar.getInstance();
		calendar.add(Calendar.DAY_OF_YEAR, 7);
		//创建builder对象
		Builder builder = JWT.create()
								//设置payload内容
								.withClaim("userInfo", user_string)
								.withExpiresAt(calendar.getTime());
		//生成token
		String token = builder.sign(Algorithm.HMAC256(key));
		
		return token;
	}
	
	//校验token
	public static DecodedJWT verify(String tokenToVerity) {
		
		DecodedJWT verify = null;
		try{
			verify = JWT
					.require(Algorithm.HMAC256(key))
					.build()
					.verify(tokenToVerity);
		}catch(TokenExpiredException e) {
			e.printStackTrace();
			System.out.println("该token已经过期。");
		}catch (SignatureVerificationException e) {
			e.printStackTrace();
			System.out.println("签名不一致");
		}catch (AlgorithmMismatchException e) {
			e.printStackTrace();
			System.out.println("签名算法不匹配");
		}catch (InvalidClaimException e) {
			e.printStackTrace();
			System.out.println("payload不可用");
		}catch (Exception e) {
			e.printStackTrace();
			System.out.println("其他错误校验失败");
		}
		
		return verify;
	}
	
	//将DecodedJWT中的信息解析出来进行比对
	public static User parse(DecodedJWT decodeJWT) throws JsonMappingException, JsonProcessingException {
		
		Claim claim = decodeJWT.getClaim("userInfo");
		if(claim!=null) {
			String userString = claim.asString();
			ObjectMapper jackson = new ObjectMapper();
			User user = jackson.readValue(userString, User.class);
			return user;
		}
		
		return null;
	}
}

10. 后台发送到前端,前端如何进行存储?


前端存储有两种方式:

  • 通过cookie来存储。
  • 通过浏览器本地存储来存储。

在这里插入图片描述

11. 前端如何将受到的token返还服务器?


前端存储token后,之后每次请求都携带这个token,用于让服务端校验。

如何携带token?

  • Authorization:授权,将jwt放到请求头里面。
  • 请求参数里面,参数名字无所谓,一般的都叫做token。

一般我们通过请求头来操作。例如:设置一个Authorization请求头获取本地存储的token,放到里面;传到服务器进行验证。

token的使用一般我们都是进行前后端分离的情况下使用。


前端发送token的几种常用的方式:

  • 对于同源的tomcat项目,一般我们使用cookie作为载体来携带token令牌发给服务器。
  • 对于跨源的,cookie就没法实现了。只能手动的通过ajax的beforeSend参数来设置请求头,进而向后台发送token令牌信息。但是,当第一次访问该页面的http请求协议是没办法携带token令牌的。
  • 在Vue中,可以通过特定的方式来实现第一次访问也可以携带token的。

对于ajax我们可以通过beforeSend参数的xhr对象来设定请求头。
在这里插入图片描述

12. 设置服务器允许跨域


package com.itholmes.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletResponse;
@WebFilter("/*")
public class CorsFilter implements Filter{

	public void init(FilterConfig filterConfig) throws ServletException {
		// TODO Auto-generated method stub
		
	}

	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		//设置跨域问题。
		HttpServletResponse resp = (HttpServletResponse)response;
		resp.addHeader("Access-Control-Allow-Origin", "*");
		resp.addHeader("Access-Control-Allow-Headers", "*");
		resp.addHeader("Access-Control-Allow-Methods", "*");
		resp.addHeader("Access-Control-Max-Age", "3600");
		resp.addHeader("Access-Control-Allow-Credentials", "false");
		chain.doFilter(request, resp);
	}

	public void destroy() {
		// TODO Auto-generated method stub
		
	}

}

13. JWT三部分组成详解


在这里插入图片描述
(这里的secret就是盐,这个盐我们可以理解为将这些东西混杂在了一起。)

其实JWT实现过程很简单,也可以自己手写一个JWT来进行操作。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xupengboo

你的鼓励将是我创作最大的动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值