• jwt以及如何使用jwt实现登录


    jwt的使用和使用jwt进行登录

    什么是jwt

    jwt是 JSON WEB TOKEN英文的缩写,它是一个开放标准,它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全传输信息。该信息可以被验证和信任,因为它是数字签名用的。
    

    使用jwt最多的场景就是登陆,一旦用户登陆,那么后续的每个请求都应该包含jwt。

    jwt的组成

    jwt由三部分组成,每一部分之间用符号"."进行分割,整体可以看做是一个长字符串。
    一个经典的jwt的样子:
    	xxx.xxx.xxx
    

    jwt的三部分分别是:

    • Header 头部

      • 头部由两部分组成:第一部分是声明类型,在jwt中声明类型就jwt,第二部分是声明加密的算法,加密算法通常使用HMAC SHA256

      • 一个经典的头部:

        {
          'typ': 'JWT',      //  'typ':'声明类型'
          'alg': 'HS256'	//	'alg':'声明的加密算法'
        }
        
    • Payload 载体、载荷

      • 这一部分是jwt的主体部分,这一部分也是json对象,可以包含需要传递的数据,其中jwt指定了七个默认的字段选择,这七个字段是推荐但是不强制使用的:
        iss:发行人
        exp:到期时间
        sub:主题
        aud:用户
        nbf:在此之前不可用
        iat:发布时间
        jti:JWT ID用于识别该JWt

      • 除了上述的七个默认字段之外,还可以自定义字段,通常我们说JWT用于用户登陆,就可以在这个地方放置用户的id和用户名

      • 下面这个json对象是一个jwt的Payload部分

        {
        
        "sub": "1234567890",
        
        "nickname": "leileilei",
        
        "id": "1234123"
        
        }
        
        • 这里注意虽然可以放自定的信息,但是不要存放一些敏感信息,除非是加密过的,因为这里的信息可能会被截获
    • signature 签证

      • 这部分是对前两部分进行base64编码在进行加密,这个加密的方式使用的是jwt的头部声明中的加密方式,在加上一个密码(secret)组成的,secret通常是一个随机的字符串,这个secret是服务器特有的,不能够让其他人知道。这部分的组成公式是:

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

    为什么选择jwt

    session的缺点
    • 首先在我的认知里jwt用处最多的就是作为用户登陆的凭证,以往这个凭证是使用session和cookie进行存储的,session技术的存储在服务器端的一种技术,构造一个类似于哈希表存储用户id和用户的一些信息,将这个用户id放在cookie里返回给用户,用户每次登陆的时候带上这个cookie,在哈希表中如果可以查到信息,那么说明用户登陆并且得到对应用户的信息。
    • 但是session存放在服务器端,当用户量很大时,占用了服务器过多的宝贵的内存资源。同时因为如果有多台服务器,那么当用户登陆时访问了服务器A,那么就只有服务器A上会存储这个用户的信息,当用户访问其他页面时,也许请求会发给服务器B,这时服务器B中是没有用户的信息的,会判定用户处于非登录的状态。也就是说session无法很好的在微服务的架构之中使用。
    • 因为session是和cookie结合使用的,如果cookie被截获,那么就会存在安全危机。
    jwt的优点
    • json形式,而json非常通用性可以让它在很多地方使用
    • jwt所占字节很小,便于传输信息
    • 需要服务器保存信息,易于扩展

    一个jwt的工具类

    如果需要使用jwt可以直接拿去使用

    package com.lei.commonutils;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jws;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Date;
    
    /**
     * @author leileilei
     * @since 2019/10/16
     */
    public class JwtUtils {
    
        //常量
        public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间
        public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //秘钥,加盐
    
         //	@param id 当前用户ID
         //	@param issuer 该JWT的签发者,是否使用是可选的
         //	@param subject 该JWT所面向的用户,是否使用是可选的
         //	@param ttlMillis 什么时候过期,这里是一个Unix时间戳,是否使用是可选的
         //	@param audience 接收该JWT的一方,是否使用是可选的
        //生成token字符串的方法
        public static String getJwtToken(String id, String nickname){
    
            String JwtToken = Jwts.builder()
                    .setHeaderParam("typ", "JWT")	//头部信息
                    .setHeaderParam("alg", "HS256")	//头部信息
    				//下面这部分是payload部分
                		// 设置默认标签
                    .setSubject("leileilei")	//设置jwt所面向的用户
                    .setIssuedAt(new Date())	//设置签证生效的时间
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))	//设置签证失效的时间
    					//自定义的信息,这里存储id和姓名信息
                    .claim("id", id)  //设置token主体部分 ,存储用户信息
                    .claim("nickname", nickname)
    				//下面是第三部分
                    .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                    .compact();
    		// 生成的字符串就是jwt信息,这个通常要返回出去
            return JwtToken;
        }
    
        /**
         * 判断token是否存在与有效
         * 直接判断字符串形式的jwt字符串
         * @param jwtToken
         * @return
         */
        public static boolean checkToken(String jwtToken) {
            if(StringUtils.isEmpty(jwtToken)) return false;
            try {
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 判断token是否存在与有效
         * 因为通常jwt都是在请求头中携带,此方法传入的参数是请求
         * @param request
         * @return
         */
        public static boolean checkToken(HttpServletRequest request) {
            try {
                String jwtToken = request.getHeader("token");//注意名字必须为token才能获取到jwt
                if(StringUtils.isEmpty(jwtToken)) return false;
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 根据token字符串获取会员id
         * 这个方法也直接从http的请求中获取id的
         * @param request
         * @return
         */
        public static String getMemberIdByJwtToken(HttpServletRequest request) {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return "";
            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            Claims claims = claimsJws.getBody();
            return (String)claims.get("id");
        }
    }
    
    

    将jwt和登录进行结合

    controller层

    	//实现前端登录
        @PostMapping("login")
        public R login(@RequestBody UcenterMember ucenterMember){
            //ucenterMember封装了手机号和密码
            //在service里面进行验证
            //并且需要返回token,作为单点登录的凭证
            String token = ucenterMemberService.login(ucenterMember);
            return R.ok().data("token",token);
        }
    

    service层

    	//判断登录
        @Override
        public String login(UcenterMember ucenterMember) {
            //首先获取到正在进行登录的手机和密码
            String mobile = ucenterMember.getMobile();
            String password = ucenterMember.getPassword();
    
            //判断手机号和密码是否是空值
            if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){
                throw new DiyException(20001,"用户名或者密码为空");
            }
    
            //判断是否存在这个用户
            QueryWrapper<UcenterMember> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("mobile",mobile);
            UcenterMember member = baseMapper.selectOne(queryWrapper);
    
            //如果这个member对象是空,那么说明非法访问
            if(member == null){
                throw new DiyException(20001,"该用户不存在");
            }
    
            if(!member.getPassword().equals(MD5.encrypt(password))){
                throw new DiyException(20001,"用户名或者密码错误");
            }
    
            //走到这里说明登录是成功的
            //生成token,使用封装好的工具类
            //将该用户的id和用户名放入到token中
            String token = JwtUtils.getJwtToken(member.getId(), member.getNickname());
            return token;
        }
    

    当存在某些需求,需要用户登录之后再进行操作可以使用如下代码

    	//根据课程id进行下单操作
        //最后需要返回订单号,通过订单号生成支付的二维码
        @GetMapping("toBuy/{courseId}")
        public R toBuy(@PathVariable String courseId, HttpServletRequest request){
            //通过jwt工具类,在request中获取到用户id
            String userId = JwtUtils.getMemberIdByJwtToken(request);
            //如果没有登录则无法购买
            if(StringUtils.isEmpty(userId)){
                return R.error().message("请先登录后在进行操作");
            }
            //确认为登录状态之后往下进行操作
            String orderNo = orderService.toBuyGetOrderNo(courseId,userId);
            return R.ok().data("orderNo",orderNo);
        }
    

    axios方式将jwt放在header中

    import axios from 'axios'
    import { MessageBox, Message } from 'element-ui'
    import cookie from 'js-cookie'
    
    // 创建axios实例
    const service = axios.create({
      baseURL: 'http://localhost:9002', // api的base_url
      timeout: 20000 // 请求超时时间
    })
    
    //第三步,将cookie中的token放入到header(请求头)中
    // http request 拦截器
    
    service.interceptors.request.use(
      config => {
      //debugger
        if (cookie.get('token')) {
          config.headers['token'] = cookie.get('token');
        }
          return config
        },
        err => {
        return Promise.reject(err);
    })
    

    前端的js代码

    	//实现登录
          //第一步调用接口实现登录
          submitLogin(){
            loginApi.login(this.user)
              .then(response => {
                  //第二步,获取到token,并将token放入到cookie中
                  cookie.set('token',response.data.data.token,{domain: 'localhost'})
    
                  //第四步,请求接口获得用户数据
                  loginApi.getInfoByToken()
                    .then(response => {
                      this.loginInfo = response.data.data.userInfo
                      //获取返回用户信息,放到cookie里面
                      cookie.set('ucenter',this.loginInfo,{domain: 'localhost'})
    
                      //跳转页面
                      window.location.href = "/";
                    })
              })
          },
    
  • 相关阅读:
    numpy计算路线距离
    WebApi安全性 使用TOKEN+签名验证
    从SQL查询分析器中读取EXCEL中的内容
    Autofac应用总结
    Visual Studio提示“无法启动IIS Express Web服务器”的解决方法
    架构 : 三层架构、MVC、MVP、MVVM
    Asp.Net MVC :路由器
    myeclipse10安装egit和使用
    myeclipse10.7安装git插件
    SQLite之C#连接SQLite
  • 原文地址:https://www.cnblogs.com/jinjidelei/p/14507889.html
Copyright © 2020-2023  润新知