1:cookie-session技术需要浏览器支持,response返回set-cookie属性后。http请求request 会自动携带cookie
而,jwt和toekn一样 每次ajax都需要手动携带,一般是设置的request的header头中
比如spring-redis 把session存在redis中,集群环境每个节点也是连接redis,tomcat不存储session,这样就可以实现集群了。
cookie-session自带刷新功能,而且浏览器会自动携带
传统的web应用还是使用cookie-session技术方便
JWT是json web token缩写,JWT包含三个部分: Header头部,Payload负载和Signature签名
jwt的思想为去中心化。生成的令牌携带了加密的用户信息 直接后台验证 ,一旦生成后只要没到设置的失效时间就一直有效,这样如果泄露后无法让该令牌立即失效,除非
在加一层拦截验证,这样就失去了使用jwt的意义。
token就是一个用户标识(key),每次传入后台使用token查询redis或者数据库等查询用户信息验证,这样token泄露后可以及时在redis或数据库中剔除该toekn
使用jwt和toekn都没类似cookie-session刷新过期时间的机制(比如session超时时间30分钟,在29分钟时使用session后,超时时间又会重置为30分钟),因为jwt的过期时间是生成jwt令牌时指定的 ,需要自己采取相应方案,防止用户使用一段时间到期了,需要重新登录生成jwt或toekn。
jwt 口令如果快过期了,是没有设置延长过期时间的方式,因为过期时间本身就包含在了jwt口令中,只能重新生成新的jwt返回给用户使用,依此循环。
普通token的方式,因为token只是一个唯一标识,用户信息,过期时间在后端,直接延长就行。
我在开发移动app中就是使用的token技术 ,原生app都有提供获取机器唯一标识 UUID的函数,登录后UUID当做key,存入登录信息,
每次ajax请求携带UUID,登录后每个需要登录才能访问的请求都需要UUID查询redis或者数据库 验证用户。
jwt和token技术适合非web应用环境,比如移动端,或者发布服务给第三方调用,用来做用户验证
总结jwt和token最大的区别就是jwt令牌自带用户信息,过期时间等,token只是一个key,需要这个key去对应的存储中查找用户信息,项目上看情况使用
我使用的spring-boot项目写的
jwt的类库蛮多,我这边使用的jjwt测试
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
JwtUtil.java
package com.wying.utils;import com.JsonProcessingException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import dec.binary.Base64;import com.fasterxml.jackson.databind.ObjectMapper;pto.SecretKey;
pto.spec.SecretKeySpec;
SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;public class JwtUtil {//JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。// public static final String JWT_ID = UUID.randomUUID().toString();public static final String JWT_ID = "111111111222222222222222223333333333333";/*** 附加码加密密文 我这边用的base64编码的 使用时在解码 也可以直接定义不加密的普通密码 使用时也不用解码了*/public static final String JWT_SECRET = "YnNvZnRwZXNoaGhoaGho";public static final int JWT_TTL = 60*60*1000; //单位毫秒 这里设置一小时public static ObjectMapper mapper = new ObjectMapper();/*** 由附加码 JWT_SECRET 生成加密key** @return*/public SecretKey generalKey() {String stringKey = JWT_SECRET;// base64 密码 解密byte[] encodedKey = Base64.decodeBase64(stringKey);// 根据给定的字节数组使用AES加密算法构造一个密钥SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 创建jwt* @param id jwtid* @param issuer jwt签发人* @param subject jwt 主题 通常存放用户信息* @param ttlMillis jtw过期时间* @return* @throws Exception*/public String createJWT(String id, String issuer, String subject, long ttlMillis) throws Exception {// 指定签名的时候使用的签名算法,也就是header那部分,jjwt已经将这部分内容封装好了。SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;// 生成JWT的时间long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);// 创建payload的私有声明(根据特定的业务需要添加,如果要拿这个做验证,一般是需要和jwt的接收方提前沟通好验证方式的)Map<String, Object> claims = new HashMap<>();claims.put("uid", "1111111111");// 生成签名的时候使用的秘钥secret,切记这个秘钥不能外露哦。它就是你服务端的私钥,在任何场景都不应该流露出去。// 一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。SecretKey key = generalKey();// 下面就是在为payload添加各种标准声明和私有声明了JwtBuilder builder = Jwts.builder() // 这里其实就是new一个JwtBuilder,设置jwt的body.setClaims(claims) // 如果有私有声明,一定要先设置这个自己创建的私有的声明,这个是给builder的claim赋值,一旦写在标准的声明赋值之后,就是覆盖了那些标准的声明的.setId(id) // 设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。.setIssuedAt(now) // iat: jwt的签发时间.setIssuer(issuer) // issuer:jwt签发人.setSubject(subject) // sub(Subject):代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。.signWith(signatureAlgorithm, key); // 设置签名使用的签名算法和签名使用的秘钥// 设置过期时间if (ttlMillis >= 0) {long expMillis = nowMillis + ttlMillis;Date exp = new Date(expMillis);builder.setExpiration(exp);}return builderpact();}/*** 解密jwt** @param jwt 令牌* @return* @throws Exception*/public Claims parseJWT(String jwt) throws Exception {SecretKey key = generalKey(); //签名秘钥,和生成的签名的秘钥一模一样Claims claims = Jwts.parser() //得到DefaultJwtParser.setSigningKey(key) //设置签名的秘钥.parseClaimsJws(jwt).getBody(); //设置需要解析的jwtreturn claims;}public static void main(String[] args) throws Exception {Map<String,String> userMeesgaeMap=new HashMap<>();userMeesgaeMap.put("userId","CS001");userMeesgaeMap.put("userName","张三");userMeesgaeMap.put("idCard","330110202001010101");userMeesgaeMap.put("releId","100");String subject =mapper.writeValueAsString(userMeesgaeMap);try {JwtUtil util = new JwtUtil();String jwt = ateJWT(JWT_ID, "gaom", subject,JWT_TTL);System.out.println(ateJWT 生成的JWT :" + jwt);System.out.println("==============解密=====================");SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");Claims c = util.parseJWT(jwt);System.out.println("jwt id:"Id());//jwt idSystem.out.println("jwt签发时间:"+sdf.IssuedAt()));System.out.println("jwt过期时间:"+sdf.Expiration()));System.out.println("jwt用户信息:"Subject());System.out.println("jwt签发人:"Issuer());System.out.println("jwt 额外附加的属性uid :"("uid", String.class));} catch (Exception e) {e.printStackTrace();}}
}
业务场景:微信H5中无法下载文件,用户下载体检报告时我是先判断访问来源,如果是微信访问的话提示用户右上角在浏览器打开链接下载,在浏览器访问pdf报告下载链接由于不需要二次登录,直接传递体检编号的话存在遍历风险,传递加密的体检编号,下载时在解密可以做到遍历风险,但是这个加密字符串是一直有效的,用户保存起来可以随时下载,所以想到了jwt,用体检编号生成一个有下载期限的令牌,同时在jwt 主题subject属性中放入体检编号,jwt验证通过后解析subject在得到体检编号,同时实现体检编号加密传输和下载期限设置;
。
/*** 创建报告jwt口令* @param httpServletRequest* @param tjTjdjbDto* @return*/@GetMapping(value = "/createRporToken")public WrapperRusult createRporToken(HttpServletRequest httpServletRequest, TjTjdjbDto tjTjdjbDto) {log.info("入参tjTjdjbDto:" + tjTjdjbDto);try {//验证权限 防止水平越权Object tjDwdmbObjDto = Session().getAttribute(ConstantUtil.DW_USER_MESSAGE);if (tjDwdmbObjDto == null) {return WrapperRusult.fail("查看PDF报告失败 提示:"+(ConstantUtil.SESSION_TIME_OUT_TIPS),"");}TjDwdmbDto tjDwdmbDto = (TjDwdmbDto) tjDwdmbObjDto;/*** 设置查询条件 只能查询本单位的报告*/tjTjdjbDto.Dwbh());tjTjdjbDto.Jgid());List<TjTjdjbDto> tjTjdjbDtoList= companyReportService.selectReportByTjbh(tjTjdjbDto);if(tjTjdjbDtoList.size()==0){return WrapperRusult.fail("查看PDF报告失败 提示: tjbh:"Tjbh()+" 未检索到报告301","");}if(!(0).getDwbh().Dwbh())){return WrapperRusult.fail("查看PDF报告失败 提示: tjbh:"Tjbh()+" 不属于当前登录单位: "Mc());}//生成令牌JwtUtil util = new JwtUtil();String jwt = ateJWT(JWT_ID, "gaom", "{"tjbh":""Tjbh()+""}",JWT_TTL);String urlEncoderJwt=de(jwt);log.info(ateJWT 生成的JWT :{} url编码后:{}",jwt,urlEncoderJwt);return WrapperRusult.success(urlEncoderJwt);} catch (Exception e) {//返回异常信息 方便前端排查错误return WrapperRusult.Message());}}
/*** 下载报告 返回pdf 文件流* @param httpServletRequest* @param httpServletResponse* @param tjTjdjbDto*/@GetMapping(value = "/downReport")public void downReport(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, TjTjdjbDto tjTjdjbDto) {ByteArrayInputStream byteArrayInputStream = null;BufferedInputStream bis = null;BufferedOutputStream bos = null;log.info(" 入参tjTjdjbDto:" + tjTjdjbDto);try {String tjbhToken= TjbhToken();String urlDecoderTjbhToken=java.URLDecoder.decode(tjbhToken);log.info("tjbhToken:{} ",urlDecoderTjbhToken);JwtUtil util = new JwtUtil();/*** 如果令牌过期会直接抛出异常,不用我们手动判断是否过期 只能能执行下去得到Claims对象 就在有效期*/Claims c = util.parseJWT(urlDecoderTjbhToken);String subjectSubject();String tjbh= Bean(subject, TjTjdjbDto.class).getTjbh();log.info("tjbhToken:{} 解析到 subject:{}, 体检编号:{} ",urlDecoderTjbhToken,subject,tjbh);tjTjdjbDto.setTjbh(tjbh);String pdfBase64= PdfReportBase64(tjTjdjbDto);byte[] bytes = Decoder().decode(pdfBase64);//创建一个将byte作为缓冲区的ByteArrayInputStream对象byteArrayInputStream = new ByteArrayInputStream(bytes);//创建从底层输入流读取的读取数据的缓冲输入流对象bis = new BufferedInputStream(byteArrayInputStream);//创建一个缓冲区字节对象byte[] buffer = new byte[bis.available()];//读取缓冲区的数据ad(buffer);//清空set();// httpServletResponse.setHeader("Content-type", "application/pdf");httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + new Bytes("gb2312"), "iso-8859-1") + ".pdf");//创建从底层输出流输出页面数据的缓冲流输出对象bos = new OutputStream());bos.write(buffer);bos.flush();} catch (Exception e) {e.printStackTrace();BaseUtil.outputString(httpServletResponse,"查看PDF报告失败 异常:e"Message());} finally {try {if (bos != null) {bos.close();}if (bis != null) {bis.close();}if (byteArrayInputStream != null) {byteArrayInputStream.close();}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
如果jw令牌过期会抛出异常
本文发布于:2024-02-04 18:31:36,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170713726958368.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |