Spring Boot 集成JWT实现前后端认证的示例代码

网友投稿 304 2022-08-04


Spring Boot 集成JWT实现前后端认证的示例代码

目录前言JWT简介为什么要用JWT传统session认证存在那些弊端?JWT认证的优势JWT的数据结构HeaderPayloadSignatureSpring Boot集成JWT引入Jwt包编写jwt工具类Token认证拦截器配置拦击器登录验证流程示例代码总结

前言

小程序、H5应用的快速发展,使得前后端分离已经成为了趋势,然而系统认证却是系统的重要一部分,本文将讲解JWT如何实现前后端认证。

JWT简介

JWT(全称:json Web Token)是一个开放标准(RFC 7519),它定义了一种紧凑的、自包含的方式,用于作为JSON对象在各方之间安全地传输信息。

为什么要用JWT

传统session认证存在那些弊端?

每个用户的登录信息都会保存到服务器的Session中,随着用户的增多,服务器开销会明显增大。Session的信息存放在服务器的内存中,对于分布式应用会导致失效,虽然可以将session的信息统一存放在Redis的缓存中,但这样可能增加了复杂性。由于Session认证是基于Cookie实现,而针对于非浏览器端和手机的移动端都不适用。前后端分离系统,由于前后端存在跨域,而Cookie信息无法跨越,所以采用Session认证也是无法继续宁跨域认证。

JWT认证的优势

简洁:JWT Token数据量小,传输速度也很快。跨语言: JWT Token是以JSON加密形式保存在客户端的,所以JWT是跨语言的,任何web形式都支持。 跨平台:不依赖于cookie和session,无需将session信息存放在服务端,非常适合于分布式应用,应用于扩展。

JWT的数据结构

Header

JWT第一部分是头部分,它是一个描述JWT元数据的Json对象,通常如下所示。

{

"alg": "HS256",

"typ": "JWT"

}

alg属性表示签名使用的算法,默认为HMAC SHA256(写为HS256),typ属性表示令牌的类型,JWT令牌统一写为JWT。

Payload

JWT第二部分是Payload,也是一个Json对象,除了包含需要传递的数据,还有七个默认的字段供选择。 iss:发行人 exp:到期时间 sub:主题 aud:用户 nbf:在此之前不可用 iat:发布时间 jti:JWT ID用于标识该JWT

{

//默认字段

"sub":"主题123",

//自定义字段

"name":"java",

"isAdmin":"true",

"loginTime":"2021-12-05 12:00:03"

}

需要注意的是,默认情况下JWT是未加密的,任何人都可以解读其内容,因此如果一些敏感信息不要存放在此,以防信息泄露。JSON对象也使用Base64 URL算法转换为字符串保存。

Signature

签名哈希部分是对上面两部分数据签名,需要使用base64编码后的header和payload数据,通过指定的算法生成哈希,以确保数据不会被篡改。

Spring Boot集成JWT

引入Jwt包

io.jsonwebtoken

jjwt

0.9.1

vOasm

编写jwt工具类

public class JwtUtil

{

//创建jwt

public static String createJWT(String subject, String issue, Object claim,

long ttlMillis)

{

//当前时间

long nowMillis = System.currentTimeMillis();

//过期时间

long expireMillis = nowMillis + ttlMillis;

String result = Jwts.builder()

.setSubject(subject) //设置主题

.setIssuer(issue) //发行者

.setId(issue)//jwtID

.setExpiration(new Date(expireMillis)) //设置过期日期

.claim("user", claim)//主题,可以包含用户信息

.signWith(getSignatureAlgorithm(), getSignedKey())//加密算法

.compressWith(CompressionCodecs.DEFLATE).compact();//对载荷进行压缩

return result;

}

// 解析jwt

public static Jws pareseJWT(String jwt)

{

Jws claims;

try

{

claims = Jwts.parser().setSigningKey(getSignedKey())

.parseClaimsJws(jwt);

}

catch (Exception ex)

{

claims = null;

}

return claims;

}

//获取主题信息

public static Claims getClaims(String jwt)

{

Claims claims;

try

{

claims = Jwts.parser().setSigningKey(getSignedKey())

.parseClaimsJws(jwt).getBody();

}

catch (Exception ex)

{

claims = null;

}

return claims;

}

}

/**

* 获取密钥

*

* @return Key

*/

private static Key getSignedKey()

{

byte[] apiKeySecretBytes = DatatypeConverter

.parseBase64Binary(getAuthKey());

Key signingKey = new SecretKeySpec(apiKeySecretBytes,

getSignatureAlgorithm().getJcaName());

return signingKey;

}

private static SignatureAlgorithm getSignatureAlgorithm()

{

return SignatureAlgorithm.HS256;

}

//获取密钥,可以动态配置

public static String getAuthKey()

{

String auth = "123@#1234";

}

Token认证拦截器

Component

public class TokenInterceptor extends HandlerInterceptorAdapter

{

public static Log logger = LogManager.getLogger(TokenInterceptor.class);

@Override

public boolean preHandle(HttpServletRequest request,

HttpServletResponse response, Object handler) throws Exception

{

String uri = request.getRequestURI();

logger.info("start TokenInterceptor preHandle.." + uri);

//需要过滤特殊请求

if (SystemUtil.isFree(uri) || SystemUtil.isProtected(uri))

{

return true;

}

String metohd=request.getMethod().toString();

logger.info("TokenInterceptor request method:"+metohd);

//options 方法需要过滤

if("OPTIONS".equals(metohd))

{

return true;

}

//是否开启token认证

boolean flag = SystemUtil.getVerifyToken();

ResponseResult result = new ResponseResult();

//从请求的head信息中获取token

String token = request.getHeader("X-Token");

if (flag)

{

if(StringUtils.isEmpty(token))

{

token=request.getParameter("X-Token");

}

// token不存在

if (StringUtils.isEmpty(token))

{

result.setCode(ResultCode.NEED_AUTH.getCode());

result.setMsg(ResultCode.NEED_AUTH.getMsg());

WebUtil.writeJson(result, response);

return false;

}

else

{

Claims claims = JwtUtil.getClaims(token);

String subject = "";

if (claims != null)

{

subject = claims.getSubject();

// 验证主题

if (StringUtils.isEmpty(subject))

{

result.setCode(ResultCode.INVALID_TOKEN.getCode());

result.setMsg(ResultCode.INVALID_TOKEN.getMsg());

WebUtil.writeJson(result, response);

return false;

}

}

else

{

result.setCode(ResultCode.INVALID_TOKEN.getCode());

result.setMsg(ResultCode.INVALID_TOKEN.getMsg());

WebUtil.writeJson(result, response);

return false;

}

}

}

return true;

}

}

配置拦击器

@Configuration

public class WebConfig implements WebMvcConfigurer

{

@Resource

private TokenInterceptor tokenInterceptor;

public void addInterceptors(InterceptorRegistry registry)

{

registry.addInterceptor(tokenInterceptor).addPathPatterns("/**");

}

}

登录验证流程

示例代码

@RequestMapping("login")

public Result login(HttpServletResponse response)

{

Map map = new HashMap<>();

//

Result result = loginAuth(user);

int code = result.getCode();

//登录认证成功

if (code ==ResultCode.SUCCESS)

{

//默认为7天

Long ttlMillis = 7*1000 * 60 * 60 * 24;

//过期时间

long expreTime = System.currentTimeMillis() + ttlMillis;

String tokenKey = UUID.randomUUID().toString();

String tokenId = JwtUtil.createJWT(user.getUserId(), tokenKey,

user.getPassword(), expreTime);

map.put("expreTime", expreTime);

map.put("tokenId", tokenId);

}

else

{

logger.error("login error:" +FastJsonUtil.toJSONString(result));

}

return result;

}

总结

时代在进步,技术也在不断更新,以前采用Session+Redis实现单点登录,现在可以替换为jwt+Redis实现,我们只有不断的更新自己的技术栈,才能避免被无情的淘汰,关于使用注解方式实现和token刷新,将在后续文章中进行讲解。


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

上一篇:利用Java实现简单的猜数字小游戏(java写猜数字小游戏)
下一篇:Sharding(shardingsphere怎么读)
相关文章

 发表评论

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