基于token的身份认证 -- jwt实践

网友投稿 646 2022-10-26

基于token的身份认证 -- jwt实践

基于token的身份认证 -- jwt实践

在进行前后端项目开发时,身份认证是一个很重要的问题。

在身份认证这一块上,我们可以使用JWT来进行验证

一,JWT简述

JWT一般用于用户登录上,身份认证这种场景下,一旦用户登录完成,在接下来的每个涉及用户权限的请求中都包含JWT,可以对用户身份,路由,服务和资源的访问权限进行验证

举一个例子,假如一个电商网站,在用户登录以后,需要验证用户的地方其实有很多,比如购物车,订单页,个人中心等等,访问这些页面正常的逻辑是先验证用户权限和登录状态,如果验证通过,则进入访问的页面,否则重定向到登录页。

二,JWT对比session或cookie

在JWT之前,这样的身份认证大多数都是通过session和cookie去实现的

1.用户身份认证cookie session实现

浏览器第一次请求服务器时,服务器会为浏览器的这次会话(session)生成一个索引为session_id的会话数据存入服务器的后端数据库,然后在cookie中存入session_id封装在响应信息中,返回给浏览器保存在本地主机的cookie文件(追加写入),第二次请求时带上cookie信息去请求服务器,服务器解析出cookie中的session_id去查询后端数据库返回相应信息

缺陷

2.用户身份认证JWT实现

1.JWT结构

JWT生成的token由三部分组成,用 . 隔开

headerheader主要包括:

token类型所使用的加密算法

{ typ: "jwt", alg: "HS256"}

iss:jwt 签发者;sub:jwt 所面向的用户;aud:接收 jwt 的一方;exp:jwt 的过期时间,这个过期时间必须要大于签发时间,这是一个秒数;nbf:定义在什么时间之前,该 jwt 都是不可用的;iat:jwt 的签发时间。

signature 签名签名这一部分是指将header或payload通过秘钥进行加密后生成,秘钥存储在服务端,不会发送给任何人,所以jwt的运输方式很安全最后将三部分使用 . 连接成字符串,就是要返回给浏览器的 token 浏览器一般会将这个 token 存储在 localStorge 以备其他需要验证用户的请求使用。

2.实现身份认证

实现:

controller

@Autowired private LoginService loginService; /** * 登录 * @return */ @RequestMapping("/login") public AjaxResult login(@RequestParam("userName")String userName, @RequestParam("password")String password){ return loginService.login(userName, password); }

service:

@Slf4j@Service@Transactionalpublic class LoginServiceImpl implements LoginService { @Autowired private LoginMapper loginMapper; @Override public AjaxResult login(String userName, String password) { Guest guest = loginMapper.selectFromTbGuest(userName, password); //校验 if(guest != null){ //校验成功 //信息封装返回给前端 Map resultMap = new HashMap(); //登录成功 String token = null; //生成token try{ //加密用户名和密码(仅为参考,一般不存储用户的隐私敏感信息) String key = Base64.getEncoder().encodeToString((guest.getUserName()+","+guest.getPassword()).getBytes()); Map claims = new HashMap(); claims.put("key",key); token = JwtUtils.createJwt(claims,"admin",720000); resultMap.put("token",token); }catch (Exception e){ log.error("生成token失败"+e.getMessage()); } resultMap.put("msg","登录成功"); //封装返回信息 return new AjaxResult().ok(resultMap); }else { return new AjaxResult().error("登录失败"); } }}

jwt工具类

@Componentpublic class JwtUtils { private static String JWT_SECRET = "123456789abcdefg"; /** * 签发jwt * @param claims * @param subject 主题 * @param ttlMillis 存活时间 * @return */ public static String createJwt(Map claims, String subject, long ttlMillis){ //jwt头部中的签名算法,默认是Hsha256 SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; //jwt签发时间 long now = System.currentTimeMillis(); Date nowTime = new Date(now); //生成签名时使用的秘钥 SecretKey secretKey = generalKey(); //构造payload JwtBuilder jwtBuilder = Jwts.builder() .setClaims(claims) .setIssuedAt(nowTime) .setSubject(subject) .signWith(signatureAlgorithm,secretKey); if(ttlMillis >= 0){ //设置过期时间 Date expireTime = new Date(now + ttlMillis); jwtBuilder.setExpiration(expireTime); } // return jwtBuilder.compact(); } /** * 签名秘钥 * @return */ private static SecretKey generalKey() { //使用base64解码 byte[] data = Base64.decodeBase64(JWT_SECRET); //AES构造指定的秘钥 SecretKey secretKey = new SecretKeySpec(data,0,data.length,"AES"); return secretKey; } /** * 解析jwt获取claims数据对象 * @param jwt * @return * @throws Exception */ public static Claims parseJWT(String jwt) throws Exception { SecretKey secretKey = generalKey(); Claims claims = Jwts.parser() .setSigningKey(secretKey) .parseClaimsJws(jwt) .getBody(); return claims; }}

-设置:

@Configuration@Slf4jpublic class JwtInterceptor implements HandlerInterceptor { @Autowired private LoginMapper loginMapper; /** * 预处理方法 * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 允许跨域 response.setHeader("Access-Control-Allow-Origin", "*"); String token = request.getHeader("authorization"); //服务器内置的token,用于测试接口 if ("testToken".equals(token)) { return true; } // 1.----------- 解析jwt信息 //判断token是否为null,使用Optional防止出现空指针异常(optional可以接受null值) Optional.ofNullable(token) .map(n -> { //判断token是否过期,过期则抛出异常 try { log.info("----->"+n); return JwtUtils.parseJWT(n); } catch (Exception e) { log.warn(">>>>>>>>>>>>>>>>>>token不存在!,重新授权登录!>>>>>>>>>>>>>>>>>>"); throw new RuntimeException("token不存在!,重新授权登录!"); } }); // 2.------------- 数据库交互 String subject = JwtUtils.parseJWT(token).getSubject(); if("admin".equals(subject)){ //管理员 //取出claims中的用户标识 byte[] bytes = Base64.getDecoder().decode(String.valueOf(JwtUtils.parseJWT(token).get("key"))); //byte[]转String String userInfo = new String(bytes); //分割userInfo,获取userName和password String[] strs = userInfo.split(","); //校验用户是否存在数据库 Guest guest = loginMapper.selectFromTbGuest(strs[0],strs[1]); return guest != null; } return false; }}

测试:

使用token请求资源api

总结:

JWT本身没啥难度,但是安全整体是一件比较复杂的事情,JWT只不过负责提供了一种基于token的身份验证机制,但是对于我们的用户权限,对于相应用户权限的api划分,资源的权限划分等,都不是JWT负责的,也就是说,请求验证完成,是否有权限请求对应的内容还是由用户权限决定

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Springboot 2.x RabbitTemplate默认消息持久化的原因解析
下一篇:并发编程 -- ScheduledThreadPoolExecutor
相关文章

 发表评论

暂时没有评论,来抢沙发吧~