spring boot+jwt实现api的token认证详解

网友投稿 306 2023-01-17


spring boot+jwt实现api的token认证详解

前言

本篇和大家分享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) 传递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

* @RbcqmfdYreturn

*/

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 getClRbcqmfdYaimsBody(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 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接口,得到成功的信息:

git地址: https://github.com/shenniubuxing3

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java使用DOM4j实现读写XML文件的属性和元素
下一篇:包含浏览器post测试的词条
相关文章

 发表评论

暂时没有评论,来抢沙发吧~