Java Token登录验证 使用jjwt生成和解析JWT

参考

JSON Web Tokens官网 Libraries里有各种语言的推荐包
jjwt的Github网址 JWT官网里面star最多的,所以用了
jjwt官方 生成和解析的例子
前后端分离之JWT用户认证 对JWT有详细的介绍
Java安全验证之JWT实践

依赖

 

流程

登录成功后,在Java中生成Jwt,存入数据库,然后返回给前端;前端接收到Jwt,储存起来(cookie或localStorage)。
前端调用api时放在Header的Authorization里面,后端通过过滤器Filter判断是否已登录。
没有使用框架,单纯的Html、servlet、数据库

生成和解析Jwt

其实就是官方的 然后用谷歌翻译了一波

生成jwt

//构建JWT的示例方法
private String createJWT(String id, String issuer, String subject, long ttlMillis) {
 
    //我们将用于签署令牌的JWT签名算法
    SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
     //创建时间
    long nowMillis = System.currentTimeMillis();
    Date now = new Date(nowMillis);
 
    //我们将使用我们的Api Key秘密签署您的JWT
    byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(apiKey.getSecret());
    Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
 
    //让我们设置JWT Claims 
    JwtBuilder builder = Jwts.builder().setId(id)
                                .setIssuedAt(now)
                                .setSubject(subject)
                                .setIssuer(issuer)
                                .signWith(signatureAlgorithm, signingKey);
     //builder.claim("name", "value"); //设置自定义的信息
 
    //如果已经指定,让我们添加到期日
    //过期时间
    if (ttlMillis >= 0) {
    long expMillis = nowMillis + ttlMillis;
        Date exp = new Date(expMillis);
        builder.setExpiration(exp);
    }
 
    //构建JWT并将其序列化为紧凑的URL安全字符串
    return builder.compact();
}

解析Jwt

//验证和读取JWT的示例方法
private void parseJWT(String jwt) {
 
    //如果它不是签名的JWS(如预期的那样),则该行将抛出异常
    Claims claims = Jwts.parser()         
       .setSigningKey(DatatypeConverter.parseBase64Binary(apiKey.getSecret()))
       .parseClaimsJws(jwt).getBody();
    System.out.println("ID: " + claims.getId());
    System.out.println("Subject: " + claims.getSubject());
    System.out.println("Issuer: " + claims.getIssuer());
    System.out.println("Expiration: " + claims.getExpiration());
    //claims.get("name") //获取自定义的信息
}

实例

后端

JwtUtil 工具类

package com.util;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import com.alibaba.fastjson.JSONObject;

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

public class JwtUtil {
    // token秘钥  太短会报错
    public static String SECRET = "qwerasdfdxzvdfajjlkjeiojznvxndjkfaowijeiodl";

    /**
     * 生成Jwt的方法
     * 
     * @param id
     *            用户ID
     * @param subject
     *            用户昵称
     * @param ttlMillis
     *            过期时间
     * @return Token String 凭证
     */
    public static String createJWT(String id, String subject, long ttlMillis) {
        // 签名方法 HS256
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        
        // 生成Jwt的时间
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        
        // 生成秘钥
        byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
        Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

        // 设置JWT所存储的信息 
        JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject).signWith(signingKey,
                signatureAlgorithm);
    
        //builder.claim("name", "value"); //存储自定义信息
         
        // 设置过期时间
        if (ttlMillis >= 0) {
            long expMillis = nowMillis + ttlMillis;
            Date exp = new Date(expMillis);
            builder.setExpiration(exp);
        }

        // 构建JWT并将其序列化为紧凑的URL安全字符串
        return builder.compact();
    }

    /**
     * 解析Jwt字符串
     * 
     * @param jwt
     *            Jwt字符串
     * @return Claims 解析后的对象
     */
    public static Claims parseJWT(String jwt) {
        return Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SECRET)).parseClaimsJws(jwt).getBody();
    }

    /**
     * 验证JWT
     * 
     * @param jwtStr jwt字符串
     * @return JOSNObject 解析结果<br/>
     *         &emsp;&emsp;Success 成功标识<br/>
     *         &emsp;&emsp;&emsp;&emsp;true:成功<br/>
     *         &emsp;&emsp;&emsp;&emsp;false:失败<br/>
     *         &emsp;&emsp;Claim 声明对象<br/>
     *         &emsp;&emsp;ErrCode 错误码<br/>
     *         &emsp;&emsp;&emsp;&emsp;1005:过期<br/>
     *         &emsp;&emsp;&emsp;&emsp;1004:未登录
     */
    public static JSONObject validateJWT(String jwtStr) {
        JSONObject pojo = new JSONObject();
        Claims claims = null;
        try {
            claims = parseJWT(jwtStr);
            pojo.put("Success", true);
            pojo.put("Claims", claims);
        } catch (ExpiredJwtException e) {
            pojo.put("Success", false);
            pojo.put("ErrCode", 1005);
            e.printStackTrace();
        } catch (Exception e) {
            pojo.put("Success", false);
            pojo.put("ErrCode", 1004);
            e.printStackTrace();
        }
        return pojo;
    }
}

LoginServlet 登录

package com.test;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.util.JwtUtil;

@WebServlet("/user/login")
public class LoginServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html;charset=utf-8");
        request.setCharacterEncoding("utf-8");
        PrintWriter out = response.getWriter();

        // 账号
        String Account = request.getParameter("Account");
        // 密码
        String password = request.getParameter("password");
        // 登录操作

        // 登录成功:
        // 有效时间:一天
        long ttlMillis = 24 * 60 * 60 * 1000;
        // 昵称
        String nickname = "star";
        // 生成jws
        String jws = JwtUtil.createJWT(Account, nickname, ttlMillis);

        // jws存到数据库

        // 返回给前端
        out.print(jws);
        out.flush();
        out.close();
    }
}

JwtFilter 过滤器

package com.filter;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.entity.Result;
import com.util.JwtUtil;

import io.jsonwebtoken.Claims;
// 登录、注册等操作不过滤  拦截servlet下的所有接口
@WebFilter(filterName = "JWTFilter", urlPatterns = { "/servlet/*" })
public class JWTFilter implements Filter {

    @Override
    public void destroy() {
        /* 销毁时调用 */
    }

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
            throws IOException, ServletException {
        /* 过滤方法 主要是对request和response进行一些处理,然后交给下一个过滤器或Servlet处理 */
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) resp;
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        PrintWriter out = response.getWriter();

        // jws设置在Header的Authorization里面
        String tokenStr = request.getHeader("Authorization");
        // 错误说明
        String msg = "系统异常";
        // 成功标识
        boolean flag = false;
        // 错误码
        int RtnValue = 9099;
        // 存放结果的实体类 有属性:success、errNum、message
        Result lr = null;
        // 用户未登录
        if (tokenStr == null || tokenStr.equals("")) {
            RtnValue = 1004;
            msg = "未登录";
            lr = new Result(RtnValue, flag, msg);
            out.print(JSON.toJSONString(lr));
            out.flush();
            out.close();
            return;
        }
        // 解析jws
        JSONObject pojo = JwtUtil.validateJWT(tokenStr);
        if (pojo.getBooleanValue("Success")) { // 解析成功
            Claims claims = (Claims) pojo.get("Claims");
            String Account = claims.getId();
            
            // 与数据库里的比较
            // true
            chain.doFilter(request, response);
            // false
            /*
            * RtnValue = 1004;
            * msg = "登录失效";
            * lr = new Result(RtnValue, flag, msg);
            * out.print(JSON.toJSONString(lr));
            * out.flush();
            * out.close();
            */
        } else { // 解析失败
            RtnValue = pojo.getIntValue("ErrCode");
            switch (RtnValue) {
            case 1005:
                msg = "签名过期";
                break;
            case 1004:
                msg = "未登录";
                break;
            default:
                break;
            }
            lr = new Result(RtnValue, flag, msg);
            out.print(JSON.toJSONString(lr));
            out.flush();
            out.close();
        }
    }

    @Override
    public void init(FilterConfig config) throws ServletException {
    }
}

前端

登录成功后,把获取到的Token存起来

function login() {
    var account = $('#account').val();
    var password = $('password').val();
    $.ajax({
        url: 'user/login',
        data: {
            'account': account,
            'password': password
        },
        dataType: 'json',
        success: function (result) {
            if (result.success) {
                //登录成功 把Token令牌存起来
                window.localStorage.setItem('token', result.message);
            } else {
                alert(result.message);
            }
        }
    });
};

调用api

$(function () {
    $.ajax({
        headers: {
            // 不设置的话,返回信息会出现中文乱码
            contentType: 'application/x-www-form-urlencoded; charset=utf-8',
            // 传给后端判断是否登录
            Authorization: localStorage.getItem('token')
        },
        url: 'servlet/getUserinfo',
        data: {
            'id': '1'
        },
        dataType: 'json',
        success: function(result) {
            if (result.hasOwnProperty('success')) { // 调用接口被拦截了 登录出错
                alert(result.message);
                return;
            }
            // 相对应的操作
        }
    });
});

原文链接:https://blog.csdn.net/CSDN906214391/article/details/98633667

posted @ 2021-11-16 15:44  程序员小明1024  阅读(2569)  评论(0编辑  收藏  举报