1.JWT簡介java
JSON Web Token(縮寫 JWT),是目前最流行的跨域認證解決方案。web
2.JWT的原理spring
JWT的原理是,服務器認證之後,生成一個JSON格式的對象,發回給客戶端,就像下面這樣.數據庫
{ "用戶名": "admin", "角色": "超級管理員", "到期時間": "2019-07-13 00:00:00" }
之後,客戶端與服務端通訊的時候,都要發回這個 JSON 對象。服務器徹底只靠這個對象認定用戶身份。跨域
爲了防止用戶篡改數據,服務器在生成這個對象的時候,會加上簽名(詳見後文)。服務器
服務器再也不保存任何 session 數據,也就是服務器變成無狀態了,從而比較容易實現擴展。session
3.JWT的數據結構數據結構
實際的 JWT是一個很長的字符串,中間用點(.
)分隔成三個部分。 就像下面這樣:app
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImp0aSI6IjNmMmc1N2E5MmFhIn0.eyJpYXQiOjE1NjI4MzM0MDgsImlzcyI6Imh0dHA6XC9cL3d3dy5weWcuY29tIiwiYXVkIjoiaHR0cDpcL1wvd3d3LnB5Zy5jb20iLCJuYmYiOjE1NjI4MzM0MDcsImV4cCI6MTU2MjkxOTgwOCwianRpIjoiM2YyZzU3YTkyYWEiLCJ1c2VyX2lkIjoxfQ.NFq1qQ-Z5c4pwit8ZkyWEwX6SBXmnHJcc6ZDgSD5nhU
JWT的三個部分依次以下:ide
- Header(頭部) - Payload(負載) - Signature(簽名)
4.JWT的使用方式
客戶端收到服務器返回的 JWT,能夠儲存在 Cookie 裏面,也能夠儲存在 localStorage。
此後,客戶端每次與服務器通訊,都要帶上這個 JWT。你能夠把它放在 Cookie 裏面自動發送,可是這樣不能跨域,因此更好的作法是放在 HTTP 請求的頭信息Authorization
字段裏面
5.JWT的幾個特色
(1)JWT 默認是不加密,但也是能夠加密的。生成原始 Token 之後,能夠用密鑰再加密一次。
(2)JWT 不加密的狀況下,不能將祕密數據寫入 JWT。
(3)JWT 不只能夠用於認證,也能夠用於交換信息。有效使用 JWT,能夠下降服務器查詢數據庫的次數。
(4)JWT 的最大缺點是,因爲服務器不保存 session 狀態,所以沒法在使用過程當中廢止某個 token,或者更改 token 的權限。也就是說,一旦 JWT 簽發了,在到期以前就會始終有效,除非服務器部署額外的邏輯。
(5)JWT 自己包含了認證信息,一旦泄露,任何人均可以得到該令牌的全部權限。爲了減小盜用,JWT 的有效期應該設置得比較短。對於一些比較重要的權限,使用時應該再次對用戶進行認證。
(6)爲了減小盜用,JWT 不該該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。
6.JWT功能實現
import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Map; import com.auth0.jwt.JWT; import com.auth0.jwt.JWTVerifier; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT; public class JwtHelper { // 祕鑰 static final String SECRET = "X-Token"; // 簽名由誰生成 static final String ISSUSER = "WXW"; // 簽名的主題 static final String SUBJECT = "this is my token"; // 簽名的觀衆 static final String AUDIENCE = "MINAPP"; public String createToken(Integer userId){ try { Algorithm algorithm = Algorithm.HMAC256(SECRET); Map<String, Object> map = new HashMap<String, Object>(); Date nowDate = new Date(); // 過時時間:2小時 Date expireDate = getAfterDate(nowDate,0,0,0,2,0,0); map.put("alg", "HS256"); map.put("typ", "JWT"); String token = JWT.create() // 設置頭部信息 Header .withHeader(map) // 設置 載荷 Payload .withClaim("userId", userId) .withIssuer(ISSUSER) .withSubject(SUBJECT) .withAudience(AUDIENCE) // 生成簽名的時間 .withIssuedAt(nowDate) // 簽名過時的時間 .withExpiresAt(expireDate) // 簽名 Signature .sign(algorithm); return token; } catch (JWTCreationException exception){ exception.printStackTrace(); } return null; } public Integer verifyTokenAndGetUserId(String token) { try { Algorithm algorithm = Algorithm.HMAC256(SECRET); JWTVerifier verifier = JWT.require(algorithm) .withIssuer(ISSUSER) .build(); DecodedJWT jwt = verifier.verify(token); Map<String, Claim> claims = jwt.getClaims(); Claim claim = claims.get("userId"); return claim.asInt(); } catch (JWTVerificationException exception){ // exception.printStackTrace(); } return 0; } public Date getAfterDate(Date date, int year, int month, int day, int hour, int minute, int second){ if(date == null){ date = new Date(); } Calendar cal = new GregorianCalendar(); cal.setTime(date); if(year != 0){ cal.add(Calendar.YEAR, year); } if(month != 0){ cal.add(Calendar.MONTH, month); } if(day != 0){ cal.add(Calendar.DATE, day); } if(hour != 0){ cal.add(Calendar.HOUR_OF_DAY, hour); } if(minute != 0){ cal.add(Calendar.MINUTE, minute); } if(second != 0){ cal.add(Calendar.SECOND, second); } return cal.getTime(); } }
import org.linlinjava.litemall.wx.util.JwtHelper; /** * token 管理 */ public class UserTokenManager { public static String generateToken(Integer id) { JwtHelper jwtHelper = new JwtHelper(); return jwtHelper.createToken(id); } public static Integer getUserId(String token) { if( null == token ){ return null; } JwtHelper jwtHelper = new JwtHelper(); Integer userId = jwtHelper.verifyTokenAndGetUserId(token); if(userId == null || userId == 0){ return null; } return userId; } public static void main(String[] args) { System.out.println(generateToken(9)); } }
過程: 根據請求, 解析請求頭中的token,得到到用戶信息,將用戶信息傳遞對應請求的method。
可查看原文: https://blog.csdn.net/sunshine_YG/article/details/85125373
1.自定義註解類
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) public @interface LoginUser { }
2.解析類
import com.xxx.xx.LoginUser; import com.xxx.xx.UserTokenManager; import org.springframework.core.MethodParameter; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { public static final String LOGIN_TOKEN_KEY = "X-Token"; @Override public boolean supportsParameter(MethodParameter parameter) { return parameter.getParameterType().isAssignableFrom(Integer.class) && parameter.hasParameterAnnotation(LoginUser.class); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container, NativeWebRequest request, WebDataBinderFactory factory) throws Exception { // return new Integer(1); String token = request.getHeader(LOGIN_TOKEN_KEY); if (token == null || token.isEmpty()) { return null; } return UserTokenManager.getUserId(token); } }
3.註冊配置
import com.xxx.xx.xx.LoginUserHandlerMethodArgumentResolver; import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.List; @Configuration public class WxWebMvcConfiguration implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { argumentResolvers.add(new LoginUserHandlerMethodArgumentResolver()); } // 攔截器 public void addInterceptors(InterceptorRegistry registry) { // 可設置攔截器 } }
4. 使用
import com.xxx.xxx.xxx.ResponseUtil; import com.xxx.xxx.xxx.LoginUser; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * index服務 */ @RestController @RequestMapping("/index") public class IndexController { @GetMapping("index") public Object index(@LoginUser Integer userId) { // 打印userId return ResponseUtil.ok(userId); } }
5. postman請求i攜帶
獲取完畢