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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM