shiro + jwt 实现 请求头中的 rememberMe 时间限…
2018-07-12 07:35:00来源:博客园 阅读 ()
前言:
上一篇提出, 通过修改 rememberMe 的编码来实现 rememberMe的功能的设想, 事后我去尝试实现了一番, 发现太麻烦, 还是不要那么做吧. 程序还是要越简单越好.
那功能总是要实现的啊, 总不能因为麻烦, 就把功能给砍了吧.
so, 换条路试试:
在前后端项目中, app和后台之间的登录, 并不能通过cookie来维持, 有一种常使用的技术: jwt, 这个技术, 其实就是通过在请求头或者参数中加入一个参数 token (这个token是经过jwt加密的)的方式, 来实现登录认证的.具体的原理并不讨论. jwt加密的时候, 是可以加入时间限制的.
在shiro里面, 如果我将 rememberMe 也进行jwt加密, 然后再赋值回rememberMe , 给出去. 当我从请求头中拿到rememberMe , 然后再解密成之前的数据, 不就可以了么.
实现:
一. jwt帮助类
import ccdc.zykt.model.vo.UserExt; import io.jsonwebtoken.Jwt; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.impl.DefaultClaims; import org.apache.commons.lang.time.DateUtils; import org.apache.shiro.codec.Base64; import java.util.Date; public class JWTUtil { private static final String KEY = Base64.encodeToString("jwt.key".getBytes()); public static String createJWT(String token) { Date now = new Date(); return Jwts.builder() .setSubject(token) .setIssuedAt(now) .setExpiration(DateUtils.addMinutes(now, 1)) .signWith(SignatureAlgorithm.HS512, KEY).compact(); } public static String createJWT(String token, int amount){ Date now = new Date(); return Jwts.builder().setSubject(token).setIssuedAt(now).setExpiration(DateUtils.addHours(now, amount)).signWith(SignatureAlgorithm.HS512, KEY).compact(); } public static boolean validate(String jwt){ try { Jwts.parser().setSigningKey(KEY).parse(jwt); return true; } catch (Throwable t) { return false; } } public static String validateJWT(String jwt) { try { Jwt parse = Jwts.parser().setSigningKey(KEY).parse(jwt); DefaultClaims body = (DefaultClaims) parse.getBody(); String phone = body.getSubject(); return phone; } catch (Throwable t) { return null; } } }
此处我将过期时间设置为1分钟, 在实际使用中, 可以将这个参数变成可配置的, 到时候根据实际需要, 将时间改长一些就可以了.
二. 改写HeaderRememberMeManager类
package ccdc.zykt.web.shiro.headtoken; import ccdc.zykt.web.util.JWTUtil; import com.google.common.base.Strings; import org.apache.commons.compress.utils.ByteUtils; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.codec.Base64; import org.apache.shiro.mgt.AbstractRememberMeManager; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.SubjectContext; import org.apache.shiro.web.servlet.ShiroHttpServletRequest; import org.apache.shiro.web.subject.WebSubjectContext; import org.apache.shiro.web.util.WebUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.StringUtils; import javax.servlet.ServletRequest; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; /** * 将remember放到响应头中去, 然后从请求头中解析 * @author: elvin * @time: 2018-07-05 15:11 * @desc: **/ public class HeaderRememberMeManager extends AbstractRememberMeManager { private static final transient Logger log = LoggerFactory.getLogger(HeaderRememberMeManager.class); // header 中 固定使用的 key public static final String DEFAULT_REMEMBER_ME_HEADER_NAME = "remember-me"; @Override protected void rememberSerializedIdentity(Subject subject, byte[] serialized) { if (!WebUtils.isHttp(subject)) { if (log.isDebugEnabled()) { String msg = "Subject argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to set the rememberMe cookie. Returning immediately and ignoring rememberMe operation."; log.debug(msg); } } else { HttpServletResponse response = WebUtils.getHttpResponse(subject); String base64 = Base64.encodeToString(serialized); base64 = JWTUtil.createJWT(base64); // 设置 rememberMe 信息到 response header 中 response.setHeader(DEFAULT_REMEMBER_ME_HEADER_NAME, base64); } } private boolean isIdentityRemoved(WebSubjectContext subjectContext) { ServletRequest request = subjectContext.resolveServletRequest(); if (request == null) { return false; } else { Boolean removed = (Boolean) request.getAttribute(ShiroHttpServletRequest.IDENTITY_REMOVED_KEY); return removed != null && removed; } } @Override protected byte[] getRememberedSerializedIdentity(SubjectContext subjectContext) { if (!WebUtils.isHttp(subjectContext)) { if (log.isDebugEnabled()) { String msg = "SubjectContext argument is not an HTTP-aware instance. This is required to obtain a servlet request and response in order to retrieve the rememberMe cookie. Returning immediately and ignoring rememberMe operation."; log.debug(msg); } return null; } else { WebSubjectContext wsc = (WebSubjectContext) subjectContext; if (this.isIdentityRemoved(wsc)) { return null; } else { HttpServletRequest request = WebUtils.getHttpRequest(wsc); // 在request header 中获取 rememberMe信息 String base64 = request.getHeader(DEFAULT_REMEMBER_ME_HEADER_NAME); if ("deleteMe".equals(base64)) { return null; } else if (base64 != null) { base64 = JWTUtil.validateJWT(base64); if(Strings.isNullOrEmpty(base64)){ return null; } base64 = this.ensurePadding(base64); if (log.isTraceEnabled()) { log.trace("Acquired Base64 encoded identity [" + base64 + "]"); } byte[] decoded = Base64.decode(base64); if (log.isTraceEnabled()) { log.trace("Base64 decoded byte array length: " + (decoded != null ? decoded.length : 0) + " bytes."); } return decoded; } else { return null; } } } } private String ensurePadding(String base64) { int length = base64.length(); if (length % 4 != 0) { StringBuilder sb = new StringBuilder(base64); for (int i = 0; i < length % 4; ++i) { sb.append('='); } base64 = sb.toString(); } return base64; } @Override protected void forgetIdentity(Subject subject) { if (WebUtils.isHttp(subject)) { HttpServletRequest request = WebUtils.getHttpRequest(subject); HttpServletResponse response = WebUtils.getHttpResponse(subject); this.forgetIdentity(request, response); } } @Override public void forgetIdentity(SubjectContext subjectContext) { if (WebUtils.isHttp(subjectContext)) { HttpServletRequest request = WebUtils.getHttpRequest(subjectContext); HttpServletResponse response = WebUtils.getHttpResponse(subjectContext); this.forgetIdentity(request, response); } } private void forgetIdentity(HttpServletRequest request, HttpServletResponse response) { //设置删除标示 response.setHeader(DEFAULT_REMEMBER_ME_HEADER_NAME, "deleteMe"); } }
rememberMe 在解析的时候, 就会将时间算进去, 如果超时了, 解析会返回false. 这样, 就可以为rememberMe设置一个超时时间
结果展示:
1. 登录之后, 使用postmen 尝试访问
2. 耐心等待1分钟, 然后再去访问这个接口试试
试验证明, 还是可行的.
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
下一篇:集合的总结
- DES/3DES/AES 三种对称加密算法实现 2020-06-11
- SpringBoot + Vue + ElementUI 实现后台管理系统模板 -- 后 2020-06-10
- Spring Boot 实现定时任务的 4 种方式 2020-06-10
- JSP+SSH+Mysql+DBCP实现的租车系统 2020-06-09
- Java实现的三种字符串反转 2020-06-09
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash