星星博客 »  > 

JWT的介绍与使用

一、什么是jwt

​ 来自官方的回答:

​ JSON Web Token (JWT) 是一个开放标准 ( RFC 7519 ),它定义了一种紧凑且自包含的方式,用于在各方之间作为 JSON 对象安全地传输信息。该信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用秘密(使用HMAC算法)或使用RSAECDSA的公钥/私钥对进行签名

​ 虽然 JWT 可以加密以在各方之间提供保密,但我们将重点关注签名令牌。签名令牌可以验证其中包含的声明的完整性,而加密令牌则对其他方隐藏这些声明。当使用公钥/私钥对对令牌进行签名时,签名还证明只有持有私钥的一方才是对其进行签名的一方。

​ 通俗的说,jwt就是将个人信息加密存储在一个签名令牌中,通过这个令牌验证个人信息。

二、什么时候要使用到jwt呢?

  • 授权:这是使用 JWT 最常见的场景。用户登录后,每个后续请求都将包含 JWT,允许用户访问该令牌允许的路由、服务和资源。单点登录是当今广泛使用 JWT 的一项功能,因为它的开销很小,并且能够轻松跨不同域使用。
  • 信息交换:JSON Web Tokens 是一种在各方之间安全传输信息的好方法。因为 JWT 可以被签名——例如,使用公钥/私钥对——你可以确定发件人就是他们所说的那样。此外,由于使用标头和有效负载计算签名,因此您还可以验证内容是否未被篡改。

要是仍然觉得还不是特别理解,那举一个很简单的例子。你想去一个游乐场,买个一张门票,这个门票就相当于你的jwt令牌。游乐场里的每个项目都要你出示门票才能玩,避免有人没买票翻墙混入游乐场(这里每个项目就相当于令牌允许的路由、服务和资源)。

三、jwt的组成

在其紧凑形式中,JSON Web Tokens 由用点 ( .)分隔的三个部分组成,它们是:

  • 标题
  • 有效载荷
  • 签名

四、jwt的第一个案例

因为jwt就是做登录验证的,所以我们首先要准备登录最基本的环境。

  1. 数据库建立user表

    最基本的三个字段即可,包含用户名密码即可。

  2. pom依赖配置

    准备最基本的环境,以及jwt的环境依赖

    <artifactId>spring-boot-starter-web</artifactId>
    <artifactId>spring-boot-starter-test</artifactId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <artifactId>lombok</artifactId>
    <artifactId>mysql-connector-java</artifactId>
    <artifactId>druid</artifactId>
    <artifactId>jjwt</artifactId>
    
  3. 准备好jwt的工具类

    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.Jws;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import org.springframework.util.StringUtils;
    
    import javax.servlet.http.HttpServletRequest;
    import java.util.Date;
    
    public class JwtUtil {
        public static final long EXPIRE = 1000 * 60 * 60 * 24;
        public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
    
        public static String getJwtToken(String id, String nickname){
    
            String JwtToken = Jwts.builder()
                    .setHeaderParam("typ", "JWT")
                    .setHeaderParam("alg", "HS256")
                    .setSubject("chenhang")
                    .setIssuedAt(new Date())
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                    .claim("id", id)
                    .claim("nickname", nickname)
                    .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                    .compact();
    
            return JwtToken;
        }
    
        /**
         * 判断token是否存在与有效
         * @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是否存在与有效
         * @param request
         * @return
         */
        public static boolean checkToken(HttpServletRequest request) {
            try {
                String jwtToken = request.getHeader("token");
                if(StringUtils.isEmpty(jwtToken)) return false;
                Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            }
            return true;
        }
    
        /**
         * 根据token获取会员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");
        }
    }
    
    
  4. 搭建最基本的包环境,包括entity,mapper,service,controller

    mapper:
        <select id="login" parameterType="User" resultType="User">
            select * from user where username = #{username} and password = #{password}
        </select>
    controller:
        //用户登录生成token令牌
        @PostMapping("/user/login")
        public Map<String,Object> login(@RequestBody User user){
            log.info("用户名:[{}]",user.getUsername());
            log.info("密码:[{}]",user.getPassword());
            Map<String,Object> map = new HashMap<>();
    
            try {
                User login = userService.login(user);
                //生成jwt令牌
                String token = JwtUtil.getJwtToken(user.getId(), user.getUsername());
                map.put("token",token);
                map.put("state",true);
                map.put("msg","登陆成功");
            } catch (Exception e) {
                map.put("state",false);
                map.put("msg","登陆失败");
            }
            return map;
        }
    	//通过token令牌验证是否合法
    	@PostMapping("/user/getInfo")
        public Map<String,Object> getInfo(String token){
            log.info("当前token为:[{}]",token);
            Map<String,Object> map = new HashMap<>();
            try {
                boolean b = JwtUtil.checkToken(token);
                if (b){
                    map.put("state",true);
                    map.put("msg","验证通过");
                    return map;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            map.put("state",false);
            map.put("msg","验证失败");
            return map;
        }
    
  5. 以上只是在当个接口中去验证token的合法性,若按照以上方法,则每个接口、方法都要去验证,造成代码冗余。所以在单体架构中,我们可以去写一个拦截器。在访问接口之前就通过拦截器去验证token的合法性,让代码更简洁,逻辑更清晰。

    拦截器的编写
    import com.chen.utils.JwtUtil;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.web.servlet.HandlerInterceptor;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.HashMap;
    import java.util.Map;
    
    public class JWTInterceptor implements HandlerInterceptor {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String token = request.getHeader("token");
            Map<String,Object> map = new HashMap<>();
            try {
                boolean b = JwtUtil.checkToken(token);
                if (b){
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            map.put("state",false);
            map.put("msg","请先登录!");
            String json = new ObjectMapper().writeValueAsString(map);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().println(json);
            return false;
        }
    }
    
  6. 最后将拦截器配置到spring当中。

    import com.chen.interceptors.JWTInterceptor;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new JWTInterceptor())
                    .addPathPatterns("/user/getInfo")
                    .excludePathPatterns("/user/login");
        }
    }
    

相关文章