springboot整合jwt實現身份驗證

一.JWT簡介

1.簡介

JSON Web token簡稱JWT, 是用於對應用程序上的用戶進行身份驗證的標記。也就是說, 使用 JWTS 的應用程序不再需要保存有關其用戶的 cookie 或其他session數據。此特性便於可伸縮性, 同時保證應用程序的安全。
在身份驗證過程中, 當用戶使用其憑據成功登錄時, 將返回 JSON Web token, 並且必須在本地保存 (通常在本地存儲中)。每當用戶要訪問受保護的路由或資源 (端點) 時, 用戶代理(user agent)必須連同請求一起發送 JWT, 通常在授權標頭中使用Bearer schema。後端服務器接收到帶有 JWT 的請求時, 首先要做的是驗證token。

2.JWT的格式

JWT就是一個字符串,經過加密處理與校驗處理的字符串,形式為:A.B.C
A由JWT頭部信息header加密得到
B由JWT用到的身份驗證信息json數據加密得到
C由A和B加密得到,是校驗部分

3.怎樣使用token?

可以放到HTTP請求的請求頭中,通常是Authorization字段。

4.流程圖

springboot整合jwt實現身份驗證

二.maven依賴

<code><dependencies>
<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-web/<artifactid>
/<dependency>

<dependency>
<groupid>org.springframework.boot/<groupid>
<artifactid>spring-boot-starter-test/<artifactid>
<scope>test/<scope>
/<dependency>

<dependency>
<groupid>io.jsonwebtoken/<groupid>
<artifactid>jjwt/<artifactid>
<version>0.9.0/<version>
/<dependency>

<dependency>
<groupid>com.alibaba/<groupid>
<artifactid>fastjson/<artifactid>
<version>1.2.58/<version>
/<dependency>

<dependency>
<groupid>javax.xml.bind/<groupid>
<artifactid>jaxb-api/<artifactid>
<version>2.3.0/<version>
/<dependency>
<dependency>
<groupid>javax.servlet/<groupid>
<artifactid>javax.servlet-api/<artifactid>
<version>4.0.1/<version>
/<dependency>
/<dependencies>

三.代碼部分
1.application.properties
server.port=8087
# 加密yan
jwt.secret=A0B1C2D3E4F5G6H7I8J9KALBMCNDOEPFQ0R1S2T3U4V5W6X7Y8Z9
# tocken 過期時間,單位秒
jwt.expire=300
# 需要認證的url,多個URL使用英文逗號,分割

jwt.authorised-urls=/api/**
2.建立config包,並在包下面建立JwtConfig類和JwtFilter類
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JwtConfig {
@Value("${jwt.secret}")
private String secret;

@Value("${jwt.expire}")
private long expire;

@Value("${jwt.authorised-urls}")
private String[] authorisedUrls;

@Bean
public JwtHelper jwtHelperBean() {
return new JwtHelper(secret, expire);
}

@Bean
public FilterRegistrationBean basicFilterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
JwtFilter filter = new JwtFilter(jwtHelperBean(), authorisedUrls);
registrationBean.setFilter(filter);
List<string> urlPatterns = new ArrayList<string>();
urlPatterns.add("/*");
registrationBean.setUrlPatterns(urlPatterns);
return registrationBean;
}
}
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;

/**
* JWT過濾器
*/
public class JwtFilter implements Filter {
private JwtHelper jwtHelper;
private List<string> urls = null;
private static final org.springframework.util.PathMatcher pathMatcher = new AntPathMatcher();
public JwtFilter(JwtHelper jwtHelper, String[] authorisedUrls) {
this.jwtHelper = jwtHelper;
urls = Arrays.asList(authorisedUrls);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
//SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
if ("OPTIONS".equals(httpRequest.getMethod())) {
httpResponse.setStatus(HttpStatus.NO_CONTENT.value()); // HttpStatus.SC_NO_CONTENT = 204
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token");
httpResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
}
String spath = httpRequest.getServletPath();
/*try {
// 驗證受保護的接口
for (String url : urls) {
if (pathMatcher.match(url, spath)) {
Object token = jwtHelper.validateTokenAndGetClaims(httpRequest);
if (token != null) {
chain.doFilter(request, response);
return;
}else{
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授權或者授權已經過期");
return;
}
}else{
chain.doFilter(request, response);
return;
}
}

} catch (Exception e) {
e.printStackTrace();
}*/

// 驗證受保護的接口
for (String url : urls) {
if (pathMatcher.match(url, spath)) {
Object token = jwtHelper.validateTokenAndGetClaims(httpRequest);
HttpSession session = httpRequest.getSession();
session.setAttribute("loginName", ((Map) token).get("loginName")); //將用戶名放在session中
if (token != null) {
chain.doFilter(request, response);
return;
}else{
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授權或者授權已經過期");
return;
}
}else{
chain.doFilter(request, response);
return;
}
}

chain.doFilter(request, response);
return;
}

@Override
public void destroy() {

}
}
3.建立Util工具包,並在包下建立JwtHelper工具類和JsonResult工具類
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtHelper {
private Long EXPIRATION_TIME;
private String SECRET;
private final String TOKEN_PREFIX = "Bearer";

private final String HEADER_STRING = "Authorization";

public JwtHelper(String secret, long expire) {
this.EXPIRATION_TIME = expire;
this.SECRET = secret;
System.out.println("正在初始化Jwthelper,expire="+expire);
}

public JSONObject generateToken(Map<string> claims) {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.SECOND, EXPIRATION_TIME.intValue());
Date d = c.getTime();
String jwt = Jwts.builder()
.setClaims(claims)
.setExpiration(d)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
JSONObject json = new JSONObject();
json.put("token",TOKEN_PREFIX + " " + jwt);
json.put("token-type", TOKEN_PREFIX);
json.put("expire-time",new SimpleDateFormat("yyyy-MM-dd HH:ss:mm").format(d) );
return json;
}

public Map<string> validateTokenAndGetClaims(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
System.out.println("token is:"+token);
if (token == null) {
return null;
}
Map<string> body = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
return body;
}
}
import com.alibaba.fastjson.JSONObject;
public class JsonResult {
public static JSONObject success(Object obj) {
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("message", "成功");
if (null != obj) {
json.put("obj", obj);
}
return json;
}

public static JSONObject fail(Object obj) {
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("message", "失敗");
if (null != obj) {
json.put("obj", obj);
}
return json;
}

public static JSONObject toJSONObject(Integer status, String msg, Object obj) {
JSONObject json = new JSONObject();
json.put("status", status);
json.put("message", msg);
if (null != obj) {
json.put("obj", obj);
}
return json;
}
}
4.建立controller包,並在包下建立AuthorizeController類和TestController類
import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/auth")
public class AuthorizeController {

@Autowired
private JwtHelper jwtHelper;

@PostMapping("/login")
public Object login(String loginName,String password) {
Map<string> claims = new HashMap<string>();
claims.put("loginName", loginName);
if ("1".equals(password)) {

return JsonResult.success(jwtHelper.generateToken(claims));
} else {
return JsonResult.fail("登錄帳號或者登錄密碼錯誤");
}

}
}
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/test")
public class TestController {
@PostMapping("/hello")
public String hello(String id) {
return "hello";
}
}
5.全局異常處理
建立ControllerException類來捕獲各類異常,再返回給前端


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
public class ControllerException implements ErrorController {
private final String PATH = "/error";

@Autowired
private ErrorAttributes errorAttributes;

@Override
public String getErrorPath() {
return PATH;
}

@RequestMapping(value = PATH, produces = {MediaType.APPLICATION_JSON_VALUE})
public Object handlerError(HttpServletRequest request){
Map<string> attributesMap = getErrorAttributes(request, true);
System.out.println(attributesMap.get("message"));
String msg = null;
if (attributesMap.get("message").toString().indexOf("JWT expired") != -1) msg = "JWT已過期";
else if (attributesMap.get("message").toString().indexOf("JWT strings must contain exactly 2 period characters") != -1) msg = "JWT格式錯誤";

else if(attributesMap.get("message").toString().indexOf("No message available") != -1) msg = "參數值缺失";
else msg = attributesMap.get("message").toString();
return JsonResult.toJSONObject(Integer.parseInt(attributesMap.get("status").toString()), msg, attributesMap.get("message"));
}

protected Map<string> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace){
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}
}

作者:尋找大海的魚
鏈接:https://www.jianshu.com/p/e0b2ea66c94c
來源:簡書
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<string>/<code>

代碼部分

三.代碼部分

1.application.properties

<code>server.port=8087
# 加密yan
jwt.secret=A0B1C2D3E4F5G6H7I8J9KALBMCNDOEPFQ0R1S2T3U4V5W6X7Y8Z9
# tocken 過期時間,單位秒
jwt.expire=300
# 需要認證的url,多個URL使用英文逗號,分割
jwt.authorised-urls=/api/**
/<code>

2.建立config包,並在包下面建立JwtConfig類和JwtFilter類

<code>import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration

public class JwtConfig {
@Value("${jwt.secret}")
private String secret;

@Value("${jwt.expire}")
private long expire;

@Value("${jwt.authorised-urls}")
private String[] authorisedUrls;

@Bean
public JwtHelper jwtHelperBean() {
return new JwtHelper(secret, expire);
}

@Bean
public FilterRegistrationBean basicFilterRegistrationBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
JwtFilter filter = new JwtFilter(jwtHelperBean(), authorisedUrls);
registrationBean.setFilter(filter);
List<string> urlPatterns = new ArrayList<string>();
urlPatterns.add("/*");
registrationBean.setUrlPatterns(urlPatterns);
return registrationBean;
}
}
/<string>/<string>/<code>
<code>import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.util.AntPathMatcher;
/**
* JWT過濾器
*/
public class JwtFilter implements Filter {
private JwtHelper jwtHelper;
private List<string> urls = null;
private static final org.springframework.util.PathMatcher pathMatcher = new AntPathMatcher();
public JwtFilter(JwtHelper jwtHelper, String[] authorisedUrls) {

this.jwtHelper = jwtHelper;
urls = Arrays.asList(authorisedUrls);
}

@Override
public void init(FilterConfig filterConfig) throws ServletException {
//SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this, filterConfig.getServletContext());
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setCharacterEncoding("UTF-8");
httpResponse.setContentType("application/json; charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Origin", "*");
if ("OPTIONS".equals(httpRequest.getMethod())) {
httpResponse.setStatus(HttpStatus.NO_CONTENT.value()); // HttpStatus.SC_NO_CONTENT = 204
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Headers", "Content-Type, x-requested-with, Token");
httpResponse.setHeader("Access-Control-Allow-Methods", "OPTIONS,GET,POST,DELETE,PUT");
}
String spath = httpRequest.getServletPath();
/*try {
// 驗證受保護的接口
for (String url : urls) {
if (pathMatcher.match(url, spath)) {
Object token = jwtHelper.validateTokenAndGetClaims(httpRequest);
if (token != null) {
chain.doFilter(request, response);
return;
}else{
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授權或者授權已經過期");
return;
}
}else{
chain.doFilter(request, response);
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}*/

// 驗證受保護的接口
for (String url : urls) {
if (pathMatcher.match(url, spath)) {
Object token = jwtHelper.validateTokenAndGetClaims(httpRequest);

HttpSession session = httpRequest.getSession();
session.setAttribute("loginName", ((Map) token).get("loginName")); //將用戶名放在session中
if (token != null) {
chain.doFilter(request, response);
return;
}else{
httpResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "未授權或者授權已經過期");
return;
}
}else{
chain.doFilter(request, response);
return;
}
}

chain.doFilter(request, response);
return;
}

@Override
public void destroy() {

}
}
/<string>/<code>

3.建立Util工具包,並在包下建立JwtHelper工具類和JsonResult工具類

<code>import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtHelper {
private Long EXPIRATION_TIME;
private String SECRET;
private final String TOKEN_PREFIX = "Bearer";
private final String HEADER_STRING = "Authorization";

public JwtHelper(String secret, long expire) {
this.EXPIRATION_TIME = expire;
this.SECRET = secret;
System.out.println("正在初始化Jwthelper,expire="+expire);

}

public JSONObject generateToken(Map<string> claims) {
Calendar c = Calendar.getInstance();
c.setTime(new Date());
c.add(Calendar.SECOND, EXPIRATION_TIME.intValue());
Date d = c.getTime();
String jwt = Jwts.builder()
.setClaims(claims)
.setExpiration(d)
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
JSONObject json = new JSONObject();
json.put("token",TOKEN_PREFIX + " " + jwt);
json.put("token-type", TOKEN_PREFIX);
json.put("expire-time",new SimpleDateFormat("yyyy-MM-dd HH:ss:mm").format(d) );
return json;
}

public Map<string> validateTokenAndGetClaims(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
System.out.println("token is:"+token);
if (token == null) {
return null;
}
Map<string> body = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
return body;
}
}
/<string>/<string>/<string>/<code>
<code>import com.alibaba.fastjson.JSONObject;
public class JsonResult {
public static JSONObject success(Object obj) {
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("message", "成功");
if (null != obj) {
json.put("obj", obj);
}
return json;
}

public static JSONObject fail(Object obj) {
JSONObject json = new JSONObject();
json.put("status", 200);
json.put("message", "失敗");

if (null != obj) {
json.put("obj", obj);
}
return json;
}

public static JSONObject toJSONObject(Integer status, String msg, Object obj) {
JSONObject json = new JSONObject();
json.put("status", status);
json.put("message", msg);
if (null != obj) {
json.put("obj", obj);
}
return json;
}
}
/<code>

4.建立controller包,並在包下建立AuthorizeController類和TestController類

<code>import java.util.HashMap;
import java.util.Map;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/auth")
public class AuthorizeController {

@Autowired
private JwtHelper jwtHelper;

@PostMapping("/login")
public Object login(String loginName,String password) {
Map<string> claims = new HashMap<string>();
claims.put("loginName", loginName);
if ("1".equals(password)) {

return JsonResult.success(jwtHelper.generateToken(claims));
} else {
return JsonResult.fail("登錄帳號或者登錄密碼錯誤");
}
}

}
/<string>/<string>/<code>
<code>import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/test")
public class TestController {
@PostMapping("/hello")
public String hello(String id) {
return "hello";
}
}
/<code>

5.全局異常處理

建立ControllerException類來捕獲各類異常,再返回給前端

<code>import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.servlet.error.ErrorAttributes;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@RestController
public class ControllerException implements ErrorController {
private final String PATH = "/error";

@Autowired
private ErrorAttributes errorAttributes;

@Override
public String getErrorPath() {
return PATH;
}

@RequestMapping(value = PATH, produces = {MediaType.APPLICATION_JSON_VALUE})
public Object handlerError(HttpServletRequest request){
Map<string> attributesMap = getErrorAttributes(request, true);
System.out.println(attributesMap.get("message"));

String msg = null;
if (attributesMap.get("message").toString().indexOf("JWT expired") != -1) msg = "JWT已過期";
else if (attributesMap.get("message").toString().indexOf("JWT strings must contain exactly 2 period characters") != -1) msg = "JWT格式錯誤";
else if(attributesMap.get("message").toString().indexOf("No message available") != -1) msg = "參數值缺失";
else msg = attributesMap.get("message").toString();
return JsonResult.toJSONObject(Integer.parseInt(attributesMap.get("status").toString()), msg, attributesMap.get("message"));
}

protected Map<string> getErrorAttributes(HttpServletRequest request, boolean includeStackTrace){
WebRequest webRequest = new ServletWebRequest(request);
return this.errorAttributes.getErrorAttributes(webRequest, includeStackTrace);
}
}/<string>/<string>/<code>


四.測試

springboot整合jwt實現身份驗證

springboot整合jwt實現身份驗證

3.將步驟1的token放到header裡面的Authorization參數裡面

springboot整合jwt實現身份驗證

訪問成功


分享到:


相關文章: