博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
springboot+jwt做api的token认证
阅读量:5947 次
发布时间:2019-06-19

本文共 9524 字,大约阅读时间需要 31 分钟。

本篇和大家分享jwt(json web token)的使用,她主要用来生成接口访问的token和验证,其单独结合springboot来开发api接口token验证很是方便,由于jwt的token中存储有用户的信息并且有加密,所以适用于分布式,这样直接吧信息存储在用户本地减速了服务端存储sessiion或token的压力;如下快速使用:

io.jsonwebtoken
jjwt
0.9.0
com.alibaba
fastjson
1.2.44

一般使用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类型,如下:

image
因此通常会在存储的时候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 
* @return */public static
T getValByT(String token, String encryKey, String key, Class
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
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
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
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
* @return */ public static
T getValByT(String token, String encryKey, String key, Class
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注解来加入到容器中:

@Configurationpublic 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查询接口,代码如是:

@RestControllerpublic 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
map = new HashMap<>(); map.put(WebConfig.Login_User, moUser); return JwtUtil.getTokenByJson(map, WebConfig.Token_EncryKey, WebConfig.Token_SecondTimeOut); }}

最终要获通过head传递token值来访问t01接口,得到如下结果:

image
token在有效时间后访问直接失败,从新获取token并访问t01接口,得到成功的信息:
image

转载地址:http://bzdxx.baihongyu.com/

你可能感兴趣的文章
Python中使用ElementTree解析xml
查看>>
Python LOGGING使用方法
查看>>
Dominating Patterns
查看>>
截取指定字符串
查看>>
metrics-server最新版本有坑,慎用
查看>>
linux虚拟文件系统浅析
查看>>
HBase数据压缩编码探索
查看>>
sprint计划会议总结
查看>>
团队项目冲刺1
查看>>
fon循环总是返回最后值问题
查看>>
Android新权限机制 AppOps
查看>>
“蓝桥杯”软件大赛入门训练4道题
查看>>
Unable to get the CMake version located at
查看>>
爬虫基本原理
查看>>
Heritage from father
查看>>
css选择器
查看>>
使用多线程
查看>>
Django--Uploaded Files以及Handlers
查看>>
在IIS(64位)上部署WCF服务访问Oracle数据库
查看>>
个人在 laravel 开发中使用到的一些技巧(持续更新)
查看>>