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/> *   Success 成功标识<br/> *     true:成功<br/> *     false:失败<br/> *   Claim 声明对象<br/> *   ErrCode 错误码<br/> *     1005:过期<br/> *     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