Token-简单使用

Posted CaoPengCheng&

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Token-简单使用相关的知识,希望对你有一定的参考价值。

Token-简单使用

(1)登录 -> 后端服务 -> 生成令牌(sajkbcjy123nmb) -> 存入Redis缓存数据库中(30分钟) -> 响应生成的令牌给前端
(2)前端请求(携带令牌) -> 后端服务 -> Redis缓存(判断令牌是否存在)

【1】token类型

(1)传统的token
传统的token是某个用户登陆之后,服务器返回一个token给用户保存,这个token可能是随机几个字母的组合,并且服
务器保留同一份token(比如用redis存储token)
当用户对其他的接口访问时,必须携带这个token,接着服务器判断这个token是否存在与redis中,来判断用户是否已
经登陆或者是否有相应的权限
(2)JWT(json web token)
与传统token不同的是,json web token是不用保留一份在服务器上的。
处理流程:
[1]用户登陆之后,服务器通过计算,返回一个按照一定规则加密过的token,token里面包含用户的一些必要信息,
比如用户的id、token过期时间
[2]当用户访问其他的接口时,必须携带这个token,接着服务器通过密码来验证这个token
[3]验证token是否是服务器发送的token
验证token过期时间
一切都正确之后,才认为用户是已经登陆、有相应权限的

什么地方会用到token?
在我们前后端分离的开发中,往往需要为多端服务提供认证,比如移动端、web端、小程序端,用户千千万个,接口千千万个,而我们后台怎么样才能保证每个接口只允许有权限的用户来访问呢?(比如只能管理员访问或者只允许登陆的用户访问),处理办法就是可以返回一个JWT给用户了,然后再从这个token里面判断当前访问接口用户是否符合我们设定

【2】Json Web Token的特点

优点
不用储存用户的登录状态信息到服务器上面(比如redis)
同一个Token在不同的服务器器、环境、甚至不同的业务服务都可以使用(只要配置好密钥之类的)
可以手机端、web端、小程序端等等使用
缺点
当然坏处也是有的
比如生成和解析token都需要时间,对比于传统的token验证方式,JWT的生成与解析花的时间会比较多
比如不能取消已经发布存在的token(如果token泄露,在token有效期内,即使用户已经改了密码,攻击者仍然可以拿该tokne为所欲为)

【3】Token的原理

Json web token 包含三部分,头部、数据、签名

(1)头部
头部可以放一些关于token的信息,比如加密算法的信息,创建token的时间,过期的时间,然后用base64进行加密
注意(base64加密后的数据是任何人都可以解密,相当于透明)
{
“typ”: “jwt”, # token类型
“iat”: 1603426198, # 生成时间
“exp”: 1603426298 # 过期时间
}

(2)数据
数据这里可以放一些用户的id、权限等级之类的,不过请不要放隐秘的数据,比如(用户的密码、用户的真实姓名)
注意(因为头部和数据这两部分是任何人都可以使用base64解密的)

{
  "say": "我爱你",
  "from": "音宫",
  "to": "小姐姐",
  "uid": 1,
  "role": "admin"
}

(3)签名
签名这部分才是JWT的精华之处,把头部和数据两段字符串进行哈希加密,而这个哈希加密跟base64加密不一样,哈希加密是需要密码的,这个密码不能透露给用户,也就是加密后的内容是不可逆的

比如加密方式如下

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)


比如头部部分记录的是7点创建,8点过期,即有效期一个小时。
数据部分记录的是某个用户的id。
这个时候服务器通过密码加密之后,会形成一串字符串(只要头部和数据部分的数据不改变,那么每次加密都会形成一样的字符串)
你7-8点之间访问服务接口都可以正常使用
然而,9点钟的时候,黑客发现了你这个token,不过token已经过期,然后黑客改了头部的过期时间,然后再用这个token来访问服务接口
这时候就会出错,为什么呢?
因为头部的信息改动之后,签名就会改变,然而黑客没有生成token的密码,使用黑客不能构造正确的签名
后台验证token的时候,会把头部和数据部分进行用密码加密,生成一个签名,然后再与提交上来的token对比是否一致

【4】生成Token

(1)生成Token

	String token = UUID.randomUUID()+"";
	System.out.println(token);
	
	6c75df3f-16f0-47da-8b73-3dd59b843cb0
UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦
为开放软件基金会组织在分布式计算环境领域的一部分。其目的,是让分布式系统中的所有元
素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。如此一来,每个人
都可以创建不与其它人冲突的UUID。在这样的情况下,就不需考虑数据库创建时的名称重复问
题。目前最广泛应用的UUID,是微软公司的全局唯一标识符(GUID),而其他重要的应用,则
有Linux ext2/ext3文件系统、LUKS加密分区、GNOME、KDE、Mac OS X等等。另外我们也可
以在e2fsprogs包中的UUID库找到实现。

(2)存入Redis

 @Resource
	 private RedisTemplate<String,Object> redisTemplate;

	String token = UUID.randomUUID()+"";
	//以token为key,user为value,存30分钟
	redisTemplate.opsForValue().set(token,user,Duration.ofMiutes(30L));

(3)返回token

	return new Result(token,"登录成功"200);
@Resource和@Autowired注解都是用来实现依赖注入的。只是@AutoWried按by type自动注入,而@Resource默认按byName自动注入。

【5】使用

(1)获取当前登录用户信息

	@Resource
	private RedisTemplate<String,Object> redisTemplate;

	//将token放在请求头传给后端
	@GetMapping("/getUserOfLogin")
	public Result getUserOfLogin(HttpServletRequest request)throw ...{
		String token = request.getHeader("token");

		Object user = redisTemplate.opsForValue.get(token);
		if(user!=null){
			return new Result(user,"获取登录用户成功",200);
		}
		return new Result(null,"获取登录用户失败",104);
	}

(2)过滤器

@WebFilter(urlPatterns = {"/view/*"})
public class LoginFilter implements Filter {

	@Resource
	private RedisTemplate<String,Object> redisTemplate;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException{
	}

	@Override
	public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse){
		//获取Headers中获取token
		String token = request.getHeaders("token");
		token = token == null ? "" : token;

		//查询token在Redis中的剩余时间
		Long expire = redisTemplate.getExpire(token);
		if(expire > 0){//是登录状态
			//重置token的时间
			redisTemplate.expire(token,30L,TimeUnit.MINUTES);
			//已登录,放行
			filterChain.doFilter(sercletRequest,servletResponse);
		}else{
			//未登录,响应数据
			String string = JSONObject.toJSONString(new Result(null,"未登录"100));
			response.setContentType("json/text;cahrset=utf-8");
			PrintWriter out = reponse.getWriter();
			out.write(string);
		}
	}
}

public class LoginController{
	@Resource
	private RedisTemplate<String,Object> redisTemplate;

	//将token放在请求头传给后端
	@GetMapping("/view/getUserOfLogin")
	public Result getUserOfLogin(HttpServletRequest request)throw ...{
		String token = request.getHeader("token");

		Object user = redisTemplate.opsForValue.get(token);
		if(user!=null){
			return new Result(user,"获取登录用户成功",200);
		}
		return new Result(null,"获取登录用户失败",104);
	}
}

以上是关于Token-简单使用的主要内容,如果未能解决你的问题,请参考以下文章

Laravel:如何在控制器的几种方法中重用代码片段

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段

简单介绍vue获取token实现token登录的示例代码

在片段中设置文本颜色

科特林 | Retrofit API 使用 access_token 获取调用

CPNtools协议建模安全分析---实例变迁标记