springboot+jwt做api的token认证
本篇和大家分享jwt(json web token)的使用,她主要用来生成接口访问的token和验证,其单独结合springboot来开发api接口token验证很是方便,由于jwt的token中存储有用户的信息并且有加密,所以适用于分布式,这样直接吧信息存储在用户本地减速了服务端存储sessiion或token的压力;如下快速使用:
<!--jwt--> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.0</version> </dependency> <!--阿里 FastJson依赖--> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.44</version> </dependency>
一般使用jwt来达到3种结果:
- 生成token
- 验证token是否有效
- 获取token中jwt信息(主要用户信息)
生成token
引入了jjwt依赖后,要生成token很方便;对于一个token来说,代表的是唯一并且不可逆的,因此我们在生成时需要增加一些唯一数据进去,比如下面的id:
long currentTime = System.currentTimeMillis(); return Jwts.builder() .setId(UUID.randomUUID().toString()) .setIssuedAt(new Date(currentTime)) //签发时间 .setSubject("system") //说明 .setIssuer("shenniu003") //签发者信息 .setAudience("custom") //接收用户 .compressWith(CompressionCodecs.GZIP) //数据压缩方式 .signWith(SignatureAlgorithm.HS256, encryKey) //加密方式 .setExpiration(new Date(currentTime + secondTimeOut * 1000)) //过期时间戳 .addClaims(claimMaps) //cla信息 .compact();
通过uuid来标记唯一id信息;当然在对token加密时需要用到秘钥,jwt很是方便她支持了很多中加密方式如:HS256,HS265,Md5等复杂及常用的加密方式;
jwt生成的token中内容分为3个部分:head信息,payload信息,sign信息,通常我们要做的是往payload增加一些用户信息(比如:账号,昵称,权限等,但不包含密码);在对jwt的token有一定了解后,我们来看下真实生成的token值:
eyJhbGciOiJIUzI1NiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAAFWMTQ7CIBSE7_LWkPDzaEsP4QnYINCIptX4INE0vbtg4sLlfPPN7HAtGWbwg1BKL4GrcbEcIwpujZF8iiEpjXFapAAG2ReYpUEcR2VxYED13Nb0ppLW3hP1eEnblqsQuiFfY0OhUrl3I70evweU_aFSejZhd7DlcDv5NTmYHUilHTD3rf_hAccHRTv--7YAAAA.i4xwoQtaWI0-dwHWN8uZ4DBm-vfli5bavYU9lRYxU5E
验证token是否有效
token生成的时都会伴随者有一个失效的时间,在这我们可以通过setExpiration函数设置过期时间,记住jwt的有效时间不是滑动的,也就是说不做任何处理时,当到达第一次设置的失效时间时,就基本没用了,要获取token是否过期可以使用如下方式:
public static boolean isExpiration(String token, String encryKey) { try { return getClaimsBody(token, encryKey) .getExpiration() .before(new Date()); } catch (ExpiredJwtException ex) { return true; } }
这里使用了date的before来用获取的过期时间和当前时间对比,判断是否继续有效,需要注意的是如果在token失效后再通过getClaimsBody(token, encryKey)获取信息,此时会报ExpiredJwtException错误,我们即可认为过期。
获取token中jwt信息(主要用户信息)
通常我们要把登录用户信息存储在jwt生成的token中,这里可以通过
addClaims(claimMaps) //cla信息
传递map来设置信息,反过来要获取token中的用户信息,我们需要这样做:
return Jwts.parser() .setSigningKey(encryKey) .parseClaimsJws(token) .getBody();
此时body获取出来是Claims类型,我们需要从中获取到用户信息,需要注意的是在addClaims存储信息的时候如果存储的map值没做过出来,那完整的实体对象存储进去后会映射成一个LinkHasMap类型,如下:
因此通常会在存储的时候json化,如下代码:
claimMaps.forEach((key, val) -> { claimMaps.put(key, JSON.toJSONString(val)); });
再来就是通过get方法获取我们存储进去的信息,并json反序列化:
/** * 获取body某个值 * * @param token * @param encryKey * @param key * @return */ public static Object getVal(String token, String encryKey, String key) { return getJws(token, encryKey).getBody().get(key); } /** * 获取body某个值,json字符转实体 * * @param token * @param encryKey * @param key * @param tClass * @param <T> * @return */ public static <T> T getValByT(String token, String encryKey, String key, Class<T> tClass) { try { String strJson = getVal(token, encryKey, key).toString(); return JSON.parseObject(strJson, tClass); } catch (Exception ex) { return null; } }
来到这里一个Jwt的Util代码基本就完成了,下面给出完整的代码例子,仅供参考:
public class JwtUtil { /** * 获取token - json化 map信息 * * @param claimMaps * @param encryKey * @param secondTimeOut * @return */ public static String getTokenByJson(Map<String, Object> claimMaps, String encryKey, int secondTimeOut) { return getToken(claimMaps, true, encryKey, secondTimeOut); } /** * 获取token * * @param claimMaps * @param isJsonMpas * @param encryKey * @param secondTimeOut * @return */ public static String getToken(Map<String, Object> claimMaps, boolean isJsonMpas, String encryKey, int secondTimeOut) { if (isJsonMpas) { claimMaps.forEach((key, val) -> { claimMaps.put(key, JSON.toJSONString(val)); }); } long currentTime = System.currentTimeMillis(); return Jwts.builder() .setId(UUID.randomUUID().toString()) .setIssuedAt(new Date(currentTime)) //签发时间 .setSubject("system") //说明 .setIssuer("shenniu003") //签发者信息 .setAudience("custom") //接收用户 .compressWith(CompressionCodecs.GZIP) //数据压缩方式 .signWith(SignatureAlgorithm.HS256, encryKey) //加密方式 .setExpiration(new Date(currentTime + secondTimeOut * 1000)) //过期时间戳 .addClaims(claimMaps) //cla信息 .compact(); } /** * 获取token中的claims信息 * * @param token * @param encryKey * @return */ private static Jws<Claims> getJws(String token, String encryKey) { return Jwts.parser() .setSigningKey(encryKey) .parseClaimsJws(token); } public static String getSignature(String token, String encryKey) { try { return getJws(token, encryKey).getSignature(); } catch (Exception ex) { return ""; } } /** * 获取token中head信息 * * @param token * @param encryKey * @return */ public static JwsHeader getHeader(String token, String encryKey) { try { return getJws(token, encryKey).getHeader(); } catch (Exception ex) { return null; } } /** * 获取payload body信息 * * @param token * @param encryKey * @return */ public static Claims getClaimsBody(String token, String encryKey) { return getJws(token, encryKey).getBody(); } /** * 获取body某个值 * * @param token * @param encryKey * @param key * @return */ public static Object getVal(String token, String encryKey, String key) { return getJws(token, encryKey).getBody().get(key); } /** * 获取body某个值,json字符转实体 * * @param token * @param encryKey * @param key * @param tClass * @param <T> * @return */ public static <T> T getValByT(String token, String encryKey, String key, Class<T> tClass) { try { String strJson = getVal(token, encryKey, key).toString(); return JSON.parseObject(strJson, tClass); } catch (Exception ex) { return null; } } /** * 是否过期 * * @param token * @param encryKey * @return */ public static boolean isExpiration(String token, String encryKey) { try { return getClaimsBody(token, encryKey) .getExpiration() .before(new Date()); } catch (ExpiredJwtException ex) { return true; } } public static String getSubject(String token, String encryKey) { try { return getClaimsBody(token, encryKey).getSubject(); } catch (Exception ex) { return ""; } } }
过滤器验证token
有了基本的JwtUtil工具,我们需要用到springboot项目中,一般来说对于登录授权token验证可以通过过滤器来操作,这里创建一个AuthenFilter,用于对post请求过来的token做验证:
public class AuthenFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest rq = (HttpServletRequest) servletRequest; HttpServletResponse rp = (HttpServletResponse) servletResponse; RpBase rpBase = new RpBase(); try { //只接受post if (!rq.getMethod().equalsIgnoreCase("post")) { filterChain.doFilter(servletRequest, servletResponse); return; } String token = rq.getHeader("token"); if (StringUtils.isEmpty(token)) { rpBase.setMsg("无token"); return; } //jwt验证 MoUser moUser = JwtUtil.getValByT(token, WebConfig.Token_EncryKey, WebConfig.Login_User, MoUser.class); if (moUser == null) { rpBase.setMsg("token已失效"); return; } System.out.println("token用户:" + moUser.getNickName()); filterChain.doFilter(servletRequest, servletResponse); } catch (Exception ex) { } finally { if (!StringUtils.isEmpty(rpBase.getMsg())) { rp.setCharacterEncoding("utf-8"); rpBase.setCode(HttpStatus.BAD_REQUEST.value()); rp.getWriter().write(JSON.toJSONString(rpBase)); } } } }
要是自定义过滤器AuthenFilter生效,还需要把她注册到容器中,这里通过编码方式,当然还可以通过@WebFilter注解来加入到容器中:
@Configuration public class WebFilterConfig { @Bean public FilterRegistrationBean setFilter() { FilterRegistrationBean registrationBean = new FilterRegistrationBean(); registrationBean.setFilter(new AuthenFilter()); registrationBean.addUrlPatterns("/api/*"); registrationBean.setOrder(FilterRegistrationBean.LOWEST_PRECEDENCE); return registrationBean; } }
注意addUrlPatterns匹配的是过滤器作用的url连接,根据需求而定;为了验证效果,这里我创建了两个接口getToken和t0,分别是获取token和post查询接口,代码如是:
@RestController public class TestController { @PostMapping("/api/t0") public String t0() throws MyException { return UUID.randomUUID().toString(); } @GetMapping("/token/{userName}") public String getToken(@PathVariable String userName) { MoUser moUser = new MoUser(); moUser.setUserName(userName); moUser.setNickName(userName); Map<String, Object> map = new HashMap<>(); map.put(WebConfig.Login_User, moUser); return JwtUtil.getTokenByJson(map, WebConfig.Token_EncryKey, WebConfig.Token_SecondTimeOut); } }
最终要获通过head传递token值来访问t01接口,得到如下结果:
token在有效时间后访问直接失败,从新获取token并访问t01接口,得到成功的信息:
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
生物智能与AI——关乎创造、关乎理解(上)
几百万年前,第一次人类智能的星火出现在非洲大陆,并且持续发展,最终在大约10万年前在智人的大脑中达到顶峰。作为现代人类,我们只能想象我们的古代祖先在窥视夜空时所经历的事情,以思考物理现实的本质,以及从内心窥视自己心理现实的本质。在过去的几百年里,我们的物种通过发现控制空间、时间、物质和能量的基本数学定律。在发展对物理现实的精确理解方面取得了巨大的智力进步,现在已经在量子力学的大框架中被编纂。然而,我们正处于探索心理现实本质的最初阶段。尤其是人类智能是如何从100亿个突触连接的1000亿个神经元的生物湿件中产生的?神经科学,心理学和认知科学等现代学科在过去100年中取得了重要进展,为解决这一重大问题奠定了基础。 但是,当涉及到我们的心智能力时,对于现代人来说,仅仅理解它们是不够的,我们非常希望在无生命系统中重现这些功能。本质上,人类作
- 下一篇
基于SSH隧道+chrome插件SwitchyOmega访问内网机器
背景:客户的服务在线下IDC,访问服务器通过VPN连接到IDC,但VPN连接后只放行了特性端口比如80,服务器部署了grafana系统之后不想与现有的80共用一个端口。比如grafana应用监听除80之外的3000端口,在不通过客户邮件申请其他端口的情况下,是否有其他办法呢?本文基于SH隧道+chrome插件SwitchyOmega访问IDC的3000端口画了架构图便于理解,如下图所示正常访问路线A,但由于防火墙的限制无法访问除允许的端口,所以我们想通过阿里云的服务器做ssh隧道(阿里云与IDC机器通过专线连接,端口未做限制),也就是图中的线路B。准备工作 :1) 准备一台与IDC互通的阿里云服务器.2) SwitchyOmega插件,官方下载地址:https://www.switchyomega.com/download/ 配置步奏:1) 配置ssh隧道(本文基于xshell实现)点击新建连接--填写服务器信息如下图隧道的配置端口可自定义。记住配置SwitchyOmega的时候要对应上。点击确定之后ssh隧道配置完毕2) 安装SwitchyOmega插件在 Chrome 地址栏输入 ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果