JWT做登录鉴权以及Token工具类

简介:JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任,因为它是数字签名的。

用处:可以用于登录授权
.
优点
支持跨域
无状态,不需要存储session的信息,减轻服务器的压力。
更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时可以采用token认证方式
无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

JWT的结构

JSON Web Token由三部分组成,它们之间用圆点(.)连接。这三部分分别是:
Header
JWT头是一个描述JWT元数据的JSON对象,alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256);typ属性表示令牌的类型,JWT令牌统一写为JWT。最后,使用Base64 URL算法将上述JSON对象转换为字符串保存

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

Payload
有效载荷部分,是JWT的主体内容部分,也是一个JSON对象,包含需要传递的数据。 JWT指定七个默认字段供选择

iss:发行人
exp:到期时间
sub:主题
aud:用户
nbf:在此之前不可用
iat:发布时间
jti:JWT ID用于标识该JWT

我们还可以在有效载荷中自定义自己想要存储的内容,下面的用法中可以看到
对payload进行Base64编码就得到JWT的第二部分
Signature
签名哈希。为了得到签名部分,你必须有编码过的header、编码过的payload、一个秘钥,签名算法是header中指定的那个,然对它们签名即可。使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。这个密钥是在服务器端指定的一个密钥,该密钥仅仅为保存在服务器中,并且不能向用户公开。

签名公式:HMACSHA256(base64UrlEncode(header)+“.”+base64UrlEncode(payload),secret)

注意:token的长度是可以变化的,你的负载信息越多,token的长度一般就会越长,所以不会存在负载不同token相同的情况,所以尽量不要在token里面存放过多的信息。

推荐文章

使用JWT做登录鉴权

引入依赖jar包

<!--jwt进行登录鉴权的依赖-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.11.0</version>
        </dependency>
package com.dongmu.util;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JwtUtil {
    /**
     * 生成jwt
     * 使用Hs256算法, 私匙使用固定JWT_SEC秘钥
     *
     * @param jwtSec    jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param ttlMillis jwt过期时间(毫秒)
     * @param username  用户名 可根据需要传递的信息添加更多, 因为浏览器get传参url限制,不建议放置过多的参数
     * @return
     */
    public static String createJWT(String jwtSec, long ttlMillis, String username) {
        // 指定签名的时候使用的签名算法,也就是header那部分
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

        // 生成JWT的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        // 创建payload的私有声明(根据特定的业务需要添加)
        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("username", username);

        // 添加payload声明
        // 设置jwt的body
        JwtBuilder builder = Jwts.builder()
                //设置头信息
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")
                //下面设置载荷信息
                // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的
                .setClaims(claims)
                // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setId(UUID.randomUUID().toString())
                // iat: jwt的签发时间
                .setIssuedAt(now)
                // 代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串
                .setSubject(username)
                // 设置签名使用的签名算法和签名使用的秘钥
                .signWith(signatureAlgorithm, jwtSec.getBytes(StandardCharsets.UTF_8));
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            // 设置过期时间
            builder.setExpiration(exp);
        }
        return builder.compact();
    }


    /**
     * Token的解密
     *
     * @param jwtSec jwt秘钥 此秘钥一定要保留好在服务端, 不能暴露出去, 否则sign就可以被伪造, 如果对接多个客户端建议改造成多个
     * @param token  加密后的token
     * @return
     */
    public static Claims parseJWT(String jwtSec, String token) {
        // 得到DefaultJwtParser
        Claims claims = Jwts.parser()
                // 设置签名的秘钥
                .setSigningKey(jwtSec.getBytes(StandardCharsets.UTF_8))
                // 设置需要解析的jwt
                .parseClaimsJws(token).getBody();
        return claims;
    }

   /* public static void main(String[] args) {
        String jwt = createJWT("beihaidongmu", 1000 * 60, "dongmu");
        System.out.println(jwt);
        Claims beihaidongmu = parseJWT("beihaidongmu", jwt);

        String id = beihaidongmu.getId();
        //获取主体
        Object dongmu = beihaidongmu.get("username");
        System.out.println(dongmu);
        //获取存留到的时间
        Date expiration = beihaidongmu.getExpiration();
        System.out.println(expiration);
        //获取id
        System.out.println(id);
    }*/
}

在拦截器中对token进行解密。解密成功则可以通过拦截器,否则不予通过。

package com.dongmu.interceptor;

import com.dongmu.controller.LoginController;
import com.dongmu.util.JwtUtil;
import com.dongmu.util.TokenUtil;
import io.jsonwebtoken.Claims;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class Interceptor implements HandlerInterceptor {

    Logger logger = LoggerFactory.getLogger(Interceptor.class);


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String token = TokenUtil.getToken(request);

        if (token==null){
            logger.info("未通过拦截器,身份验证失败了。");
            return false;
        }else {
            try {
                Claims claims = JwtUtil.parseJWT(LoginController.token_password, token);
                logger.info("通过拦截器,身份验证成功了。");
                return true;
            }catch (Exception e){
                logger.info("未通过拦截器,身份验证失败了。");
                return false;
            }
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

最后将拦截器注入到springioc的容器中

package com.dongmu.config;

import com.dongmu.interceptor.Interceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;


@Configuration
public class InteceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        String path = "/**";
        String excludePath="/login";
        String swaggerExcludePath[] = {"/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui.html/**"};



        HandlerInterceptor interceptor = new Interceptor();

        registry.addInterceptor(interceptor).addPathPatterns(path).excludePathPatterns(excludePath).excludePathPatterns(swaggerExcludePath);
    }
}

你可能感兴趣的:(java,开发语言)