• 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

  • 相关阅读:
    树链剖分学习笔记(未完)
    VI 配置文件(略全)
    linux之awk
    指针之基础篇
    linux之sed
    sqlplus命令手册
    Leetcode复习: 堆和栈
    leetcode 的shell部分4道题整理
    Regular Expression Matching [leetcode]
    深入浅出JAVA
  • 原文地址:https://www.cnblogs.com/konglxblog/p/15561523.html
Copyright © 2020-2023  润新知