放假以前作了幾個小項目+課設,都用到了token實現登陸驗證和權限判斷,然鵝當時和同組的小夥伴也都是第一次接觸到了token,因而乎都是一臉懵逼(xjbx)的寫完了登陸驗證的先後端邏輯(我寫前端,同組的小夥伴寫後端)。今天有空仔細學習了一下SpringBoot實現token認證以及和前端的交互,踩了很多坑,在這裏記錄一下前端
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.5.0</version> </dependency> 複製代碼
private static final long EXPIRE_TIME = 15 * 60 * 1000; private static final String TOKEN_SECRET = "thefirsttoken123"; 複製代碼
/** * 生成簽名,15分鐘過時 * @param **username** * @param **password** * @return */ public static String sign(String username, String password) { try { // 設置過時時間 Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME); // 私鑰和加密算法 Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); // 設置頭部信息 Map<String, Object> header = new HashMap<>(2); header.put("Type", "Jwt"); header.put("alg", "HS256"); // 返回token字符串 return JWT.create() .withHeader(header) .withClaim("loginName", username) .withClaim("pwd", password) .withExpiresAt(date) .sign(algorithm); } catch (Exception e) { e.printStackTrace(); return null; } } 複製代碼
/** * 檢驗token是否正確 * @param **token** * @return */ public static boolean verify(String token){ try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); return true; } catch (Exception e){ return false; } } 複製代碼
至此,工具類就編寫完成啦!vue
@PostMapping(value = "/login") public Map<String, Object> login(@RequestBody SysUser sysUser){ Map<String, Object> map = new HashMap<>(); String username = sysUser.getUsername(); String password = sysUser.getPassword(); if (sysUserService.login(username, password)){ String token = TokenUtil.sign(username,password); if (token != null){ map.put("code", "10000"); map.put("message","認證成功"); map.put("token", token); return map; } } map.put("code", "00000"); map.put("message","認證失敗"); return map; } 複製代碼
如今服務端給客戶端簽發token的功能已經差很少實現了。 那麼客戶端如何將token應用到之後的請求中,服務端又如何識別token呢?java
/** * 自定義token攔截器 */ @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("admin-token"); if (token != null){ boolean result = TokenUtil.verify(token); if(result){ System.out.println("經過攔截器"); return true; } } System.out.println("認證失敗"); response.getWriter().write("50000"); return false; } } 複製代碼
TokenInterceptor實現了HandlerInterceptor接口,重寫了preHandle方法,該方法是在每一個請求以前觸發執行,從request的頭裏面取出token,這裏咱們統一了存放token的鍵爲admin-token,驗證經過,放行,驗證不經過,返回認證失敗信息。 這裏有一個坑,因爲使用axios,每次前端發送請求,都會先發一次預請求,也就是RequestMethod爲OPTIONS不是咱們常見的get、post等(關於OPTIONS的解釋,能夠谷歌一下)。全部在這裏咱們須要作一次判斷,若是請求方法爲OPTIONS,就直接return經過。ios
@Configuration public class InterceptorConfig extends WebMvcConfigurerAdapter { private TokenInterceptor tokenInterceptor; public InterceptorConfig(TokenInterceptor tokenInterceptor) { this.tokenInterceptor = tokenInterceptor; } @Override public void addInterceptors(InterceptorRegistry registry) { List<String> excludePath = new ArrayList<>(); String sysUserLogin = "/api/sysUser/login"; excludePath.add(sysUserLogin); registry.addInterceptor(tokenInterceptor).excludePathPatterns(excludePath); } } 複製代碼
/** * 從token中獲取username信息 * @param **token** * @return */ public static String getUserName(String token){ try { DecodedJWT jwt = JWT.decode(token); return jwt.getClaim("loginName").asString(); } catch (JWTDecodeException e){ e.printStackTrace(); return null; } } 複製代碼
前端使用vue+axios,主要是實現對axios的再封裝。 相關代碼以下 大體邏輯就是,若是vuex中已經存在了token,那麼就把它放到請求頭中發往服務端。算法
// 建立axios實例 const service = axios.create({ baseURL: process.env.BASE_API // api 的 base_url }) // request攔截器 service.interceptors.request.use( config => { if (store.getters.token) { config.headers['admin-token'] = getToken() // 讓每一個請求攜帶自定義token } return config }, error => { // 出錯 console.log(error) Promise.reject(error) } ) 複製代碼
哇在掘金的第一篇文章寫完了,嘻嘻,繼續努力!!!。vuex
昨日今日明日日日碼不停蹄,
去年今年明年年年都是單身。
橫批:這就是命數據庫