SpringBoot学习- 4、整合JWT

时间:2023-12-22 12:01:02

SpringBoot学习足迹

1、Json web token(JWT)是为了网络应用环境间传递声明而执行的一种基于JSON的开发标准(RFC 7519),该token被设计为紧凑且安全的,特别适用于分布式站点的单点登陆(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。JWT听起来很复杂,其实原理很简单,比如我们可以自己生成一个guid字符串+key+附加信息加密作为token传递到客户端也能达到同样目的。

2、pom.xml增加如下代码来添加JWT依赖包

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.9.0</version>
</dependency>

3、IDEA2019.3版本中文乱码,设置UTF-8不起作用,修改字体如下
SpringBoot学习- 4、整合JWT

以下几部分内容主要参考
https://www.cnblogs.com/30go/p/10963924.html
https://www.jianshu.com/p/9af8612f6aef

4、在Utils文件夹下增加TokenUtil

RefreshToken暂不使用

package com.jgui.utils;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.jgui.domain.JUser; import java.util.Date; /**
* @Author: zhaogaojian
* @Description:
* @Date: Created in 2020/1/722:13
*/
public class TokenUtil {
private static final long TOKEN_EXPIRE_TIME= 30*60*1000;//超时时间30分钟
private static final long REFRESH_TOKEN_EXPIRE_TIME= 15*24*60*1000;//超时时间15天
private static final String TOKEN_SECRET="jguiafadfiierpewirpew8908ewrq";//秘钥
private static final String ISSUER="jgadmin";//签发人
/**
* 签名生成
* @param user
* @return
*/
public static String sign(JUser user){ String token = "";
try {
Date expiresAt = new Date(System.currentTimeMillis() + TOKEN_EXPIRE_TIME);
Date now = new Date();
token = JWT.create()
.withIssuer(ISSUER)
.withClaim("username", user.getUsername())
.withExpiresAt(expiresAt)
.withIssuedAt(now)
// 使用了HMAC256加密算法。
.sign(Algorithm.HMAC256(TOKEN_SECRET));
} catch (Exception e){
e.printStackTrace();
}
return token; }
/**
* 签名验证
* @param token
* @return
*/
public static boolean verify(String token){
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
DecodedJWT jwt = verifier.verify(token);
System.out.println("认证通过:");
System.out.println("issuer: " + jwt.getIssuer());
System.out.println("username: " + jwt.getClaim("username").asString());
System.out.println("签发时间:" + jwt.getIssuedAt());
System.out.println("过期时间:" + jwt.getExpiresAt());
return true;
} catch (Exception e){
return false;
} }
/**
* 从token获取username
*/
public static String getUsername(String token){
try{
return JWT.decode(token).getClaim("username").asString();
}catch(Exception ex){
ex.printStackTrace();
}
return "";
}
}
package com.jgui.utils;

import java.util.UUID;

/**
* @Author: zhaogaojian
* @Description:
* @Date: Created in 2020/1/722:50
*/
public class StringUtil {
public static String GetUUIDString()
{
return UUID.randomUUID().toString();
} }

5、在Controller下增加LoginController

package com.jgui.controller;

import com.jgui.dao.JUserDao;
import com.jgui.domain.JUser;
import com.jgui.utils.StringUtil;
import com.jgui.utils.TokenUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit; /**
* @Author: zhaogaojian
* @Description:
* @Date: Created in 2020/1/722:38
*/
@RestController
public class LoginController {
@Resource
private JUserDao userDao;
@GetMapping("/login")
public Map<String,Object> login(@RequestParam String username, @RequestParam String password) { if("zhangsan".equals(username) && "123".equals(password)){
JUser user=new JUser();
user.setUsername("zhangsan");
user.setRealname("张三");
Map<String,Object> map = new HashMap<>();
//生成token
String token = TokenUtil.sign(user);
String refreshToken = StringUtil.GetUUIDString();
if(token != ""){
map.put("code", "10000");
map.put("message", "认证成功");
map.put("token", token);
map.put("refreshtoken", refreshToken);
return map;
}
}
Map<String,Object> map = new HashMap<>();
map.put("code", "10001");
map.put("message", "认证失败");
map.put("token", "");
map.put("refreshtoken", "");
return map;
}
}

6、在Interceptor目录下增加TokenInterceptor

package com.jgui.interceptor;
import com.jgui.utils.TokenUtil;
import net.minidev.json.JSONObject;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* @Author: zhaogaojian
* @Description:
* @Date: Created in 2020/1/723:12
*/
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
@Component
public class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,Object handler)throws Exception{ if(request.getMethod().equals("OPTIONS")){
response.setStatus(HttpServletResponse.SC_OK);
return true;
}
response.setCharacterEncoding("utf-8");
String token = request.getHeader("token");
if(token != null){
boolean result = TokenUtil.verify(token);
if(result){
System.out.println("通过拦截器");
return true;
}
}
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try{
JSONObject json = new JSONObject();
json.put("success","false");
json.put("msg","认证失败,未通过拦截器");
json.put("code","10003");
response.getWriter().append(json.toJSONString());
System.out.println("认证失败,未通过拦截器");
}catch (Exception e){
e.printStackTrace();
response.sendError(500);
return false;
}
return false; } }

7、在config下增加InterceptorConfig

package com.jgui.config;

/**
* @Author: zhaogaojian
* @Description:
* @Date: Created in 2020/1/723:15
*/
import com.jgui.interceptor.TokenInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
/**
* 拦截器配置
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
private TokenInterceptor tokenInterceptor;
//构造方法
public InterceptorConfig(TokenInterceptor tokenInterceptor){
this.tokenInterceptor = tokenInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry){
List<String> excludePath = new ArrayList<>();
excludePath.add("/login"); //登录 registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns(excludePath);
WebMvcConfigurer.super.addInterceptors(registry);
}
}

8、运行项目

可以发现之前的http://localhost:8080/Hello

访问时会因为拦截器而导致无法访问

SpringBoot学习- 4、整合JWT

login接口因为被排除在拦截器外可以访问

SpringBoot学习- 4、整合JWT

SpringBoot学习- 4、整合JWT

以上内容主要参考
https://www.cnblogs.com/30go/p/10963924.html
https://www.jianshu.com/p/9af8612f6aef

截止目前项目结构如下
SpringBoot学习- 4、整合JWT

上一节:SpringBoot学习- 3、整合MyBatis