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-简单使用的主要内容,如果未能解决你的问题,请参考以下文章
Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段