JWT簡介
簡介
JSON Web token簡稱JWT轩缤, 是用于對應(yīng)用程序上的用戶進(jìn)行身份驗證的標(biāo)記疮薇。也就是說, 使用 JWTS 的應(yīng)用程序不再需要保存有關(guān)其用戶的 cookie 或其他session數(shù)據(jù)情萤。此特性便于可伸縮性, 同時保證應(yīng)用程序的安全。
在身份驗證過程中, 當(dāng)用戶使用其憑據(jù)成功登錄時, 將返回 JSON Web token, 并且必須在本地保存 (通常在本地存儲中)抑月。每當(dāng)用戶要訪問受保護(hù)的路由或資源 (端點) 時, 用戶代理(user agent)必須連同請求一起發(fā)送 JWT, 通常在授權(quán)標(biāo)頭中使用Bearer schema幼衰。后端服務(wù)器接收到帶有 JWT 的請求時, 首先要做的是驗證token。
JWT的格式
JWT就是一個字符串诊霹,經(jīng)過加密處理與校驗處理的字符串羞延,形式為:A.B.C
A由JWT頭部信息header加密得到
B由JWT用到的身份驗證信息json數(shù)據(jù)加密得到
C由A和B加密得到,是校驗部分
怎樣使用token脾还?
可以放到HTTP請求的請求頭中伴箩,通常是Authorization字段。
流程圖
JWT 實戰(zhàn)
加入Maven jwt 依賴
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
在application.proterties中加入配置
# 加密yan
jwt.secret=A0B1C2D3E4F5G6H7I8J9KALBMCNDOEPFQ0R1S2T3U4V5W6X7Y8Z9
# tocken 過期時間鄙漏,單位秒
jwt.expire=300
# 需要認(rèn)證的url赛蔫,多個URL使用英文逗號,分割
jwt.authorised-urls=/apis/fis/redis/**
JwtHelper工具類
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, Object> 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, Object> validateTokenAndGetClaims(HttpServletRequest request) {
String token = request.getHeader(HEADER_STRING);
System.out.println("token is:"+token);
if (token == null) {
return null;
}
Map<String, Object> body = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
.getBody();
return body;
}
}
JWT過濾器JwtFilter
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過濾器
*
* @author 李慶海
*
*/
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 {
// 驗證受保護(hù)的接口
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, "未授權(quán)或者授權(quán)已經(jīng)過期");
return;
}
}else{
chain.doFilter(request, response);
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
chain.doFilter(request, response);
return;
}
@Override
public void destroy() {
}
}
配置JWT
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;
import cn.com.yd.fis.client.jwt.JwtFilter;
import cn.com.yd.fis.client.jwt.JwtHelper;
@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;
}
}
在Controller中使用JWT
此處僅為說明jwt的用法泥张,在實際應(yīng)用時可以根據(jù)具體的業(yè)務(wù)需要加入不同的或者更多的參數(shù)呵恢,一并作為claims進(jìn)行參數(shù)傳遞。
import java.util.HashMap;
import java.util.Map;
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;
import cn.com.yd.fis.client.jwt.JwtHelper;
import cn.com.yd.fis.client.util.JsonResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@RestController
@RequestMapping("${api-url}/auth")
public class AuthorizeController {
@Autowired
private JwtHelper jwtHelper;
@PostMapping("/login")
public Object login(String loginName,String password) throws Exception {
Map<String, Object> claims = new HashMap<String, Object>();
claims.put("loginName", loginName);
if ("1".equals(password)) {
return JsonResult.success(jwtHelper.generateToken(claims));
} else {
return JsonResult.fail("登錄帳號或者登錄密碼錯誤");
}
}
}
輔助工具類JsonResult
import com.alibaba.fastjson.JSONObject;
public class JsonResult {
public static JSONObject success(Object obj) {
JSONObject json = new JSONObject();
json.put("state", true);
json.put("msg", "成功");
if (null != obj) {
json.put("obj", obj);
}
return json;
}
public static JSONObject fail(Object obj) {
JSONObject json = new JSONObject();
json.put("state", false);
json.put("msg", "失敗");
if (null != obj) {
json.put("obj", obj);
}
return json;
}
public static JSONObject toJSONObject(boolean state, String msg, Object obj) {
JSONObject json = new JSONObject();
json.put("state", state);
json.put("msg", msg);
if (null != obj) {
json.put("obj", obj);
}
return json;
}
}