上一篇文章《一分鍾帶你了解JWT認證!》介紹了JWT的組成和認證原理,本文將介紹下SpringBoot整合JWT實現認證的過程,帶你更深入的了解下JWT。
一、JWT認證流程
認證流程如下:
- 用戶使用賬號和密碼發出post請求;
- 服務器使用私鑰創建一個jwt;
- 服務器返回這個jwt給瀏覽器;
- 瀏覽器將該jwt串在請求頭中像服務器發送請求;
- 服務器驗證該jwt;
- 返回響應的資源給瀏覽器。
二、SpringBoot整合JWT
新建一個spring boot項目spring-boot-jwt,按照下面步驟操作。
1.pom.xml引入jar包
<!-- 引入jwt-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.8.2</version>
</dependency>
2.新建Jwt工具類
Jwt工具類進行token的生成和認證,工具類代碼如下:
/**
* @description: Jwt工具類,生成JWT和認證
* @author: Java碎碎念
*/
public class JwtUtil {
private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
/**
* 密鑰
*/
private static final String SECRET = "my_secret";
/**
* 過期時間
**/
private static final long EXPIRATION = 1800L;//單位為秒
/**
* 生成用戶token,設置token超時時間
*/
public static String createToken(User user) {
//過期時間
Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
String token = JWT.create()
.withHeader(map)// 添加頭部
//可以將基本信息放到claims中
.withClaim("id", user.getId())//userId
.withClaim("userName", user.getUserName())//userName
.withClaim("name", user.getName())//name
.withExpiresAt(expireDate) //超時設置,設置過期的日期
.withIssuedAt(new Date()) //簽發時間
.sign(Algorithm.HMAC256(SECRET)); //SECRET加密
return token;
}
/**
* 校驗token並解析token
*/
public static Map<String, Claim> verifyToken(String token) {
DecodedJWT jwt = null;
try {
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
jwt = verifier.verify(token);
} catch (Exception e) {
logger.error(e.getMessage());
logger.error("token解碼異常");
//解碼異常則拋出異常
return null;
}
return jwt.getClaims();
}
}
3.添加JWT過濾器
JWT過濾器中進行token的校驗和判斷,,token不合法直接返回,合法則解密數據並把數據放到request中供后續使用。
為了使過濾器生效,需要在啟動類添加注解@ServletComponentScan(basePackages = "com.example.springbootjwt.filter")。
JWT過濾器代碼如下:
/**
* JWT過濾器,攔截 /secure的請求
*/
@Slf4j
@WebFilter(filterName = "JwtFilter", urlPatterns = "/secure/*")
public class JwtFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
response.setCharacterEncoding("UTF-8");
//獲取 header里的token
final String token = request.getHeader("authorization");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
chain.doFilter(request, response);
}
// Except OPTIONS, other request should be checked by JWT
else {
if (token == null) {
response.getWriter().write("沒有token!");
return;
}
Map<String, Claim> userData = JwtUtil.verifyToken(token);
if (userData == null) {
response.getWriter().write("token不合法!");
return;
}
Integer id = userData.get("id").asInt();
String name = userData.get("name").asString();
String userName = userData.get("userName").asString();
//攔截器 拿到用戶信息,放到request中
request.setAttribute("id", id);
request.setAttribute("name", name);
request.setAttribute("userName", userName);
chain.doFilter(req, res);
}
}
@Override
public void destroy() {
}
}
4.添加登錄Controller
登錄Controller進行登錄操作,登錄成功后生產token並返回。
登錄Controller代碼如下:
/**
* 登錄Controller
*/
@Slf4j
@RestController
public class LoginController {
static Map<Integer, User> userMap = new HashMap<>();
static {
//模擬數據庫
User user1 = new User(1, "zhangsan", "張三", "123456");
userMap.put(1, user1);
User user2 = new User(2, "lisi", "李四", "123123");
userMap.put(2, user2);
}
/**
* 模擬用戶 登錄
*/
@RequestMapping("/login")
public String login(User user) {
for (User dbUser : userMap.values()) {
if (dbUser.getUserName().equals(user.getUserName()) && dbUser.getPassword().equals(user.getPassword())) {
log.info("登錄成功!生成token!");
String token = JwtUtil.createToken(dbUser);
return token;
}
}
return "";
}
}
5.添加SecureController
SecureController中的請求會被JWT過濾器攔截,合法后才能訪問。
SecureController代碼如下:
/**
* 需要登錄后才能訪問
*/
@Slf4j
@RestController
public class SecureController {
/**
* 查詢 用戶信息,登錄后才能訪問
*/
@RequestMapping("/secure/getUserInfo")
public String login(HttpServletRequest request) {
Integer id = (Integer) request.getAttribute("id");
String name = request.getAttribute("name").toString();
String userName = request.getAttribute("userName").toString();
return "當前用戶信息id=" + id + ",name=" + name + ",userName=" + userName;
}
}
三、測試
測試分兩步,首先訪問登錄接口,登錄成功后獲取token,然后拿着token在訪問查詢用戶信息接口。
1.訪問登錄接口
打開PostMan,訪問http://localhost:8080/login?userName=zhangsan&password=123456,登錄成功后接口返回token,請求成功截圖如下:
2.訪問用戶信息接口
打開PostMan,訪問http://localhost:8080/secure/getUserInfo,header里需要攜帶token,請求成功截圖如下:
到此SpringBoot整合JWT的功能已經全部實現,有問題歡迎留言溝通哦!
完整源碼地址: https://github.com/suisui2019/springboot-study
推薦閱讀
1.一分鍾帶你了解JWT認證!
2.SpringBoot中如何優雅的讀取yml配置文件?
3.SpringBoot中如何靈活的實現接口數據的加解密功能?
4.SpringBoot中神奇的@Enable*注解?
5.Java中Integer.parseInt和Integer.valueOf,你還傻傻分不清嗎?
限時領取免費Java相關資料,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高並發分布式、大數據、機器學習等技術。
關注下方公眾號即可免費領取: