二、使用token解決前端后端分離用戶認證問題
2.1 用戶提交帳號和密碼到服務器的認證接口
-
login.html
-
doSubmit:function(){ console.log("~~~~~~~~~~~~~doSubmit"); axios.get("http://localhost:8080/user/login",{ params:{ username:this.username, password:this.password } }).then(function(res){ if(res.data.code==0){ var token = res.data.data; //在前端存儲token document.cookie = "token="+token; //localStorage.setItem("token",token); //跳轉到index.html location.href = "index.html"; }else{ //彈出提示框:提示登錄失敗 } }); }
2.2 認證接口
-
--> 接受帳號和密碼進行認證
-
--> 生成token(如果是隨機token則需要在服務器進行存儲,如果是按照特定的協議生成則無需存儲)
-
--> 將生成的token響應給前端
- UserController
-
@RequestMapping(value = "/login",method = RequestMethod.GET) @ApiOperation(value = "用戶認證接口",notes = "調用此接口的注意事項") @ApiImplicitParams({ @ApiImplicitParam(paramType = "query",name="username", required = true,dataType = "Book"), @ApiImplicitParam(paramType = "query",name="password",required = true,dataType = "String") }) public ResultVO login(String username, String password, HttpServletRequest request) { try { User user = userService.checkLogin(username, password); if (user!=null){ //登錄成功 //request.getSession().setAttribute("user",user); //b.生成token(自定義生成規則) String token = TokenUtil.createToken(username); //c.將token回傳給前端 return new ResultVO(0,"success",token); } } catch (Exception e) { e.printStackTrace(); } return new ResultVO(1,"fail"); }
public class TokenUtil { public static String createToken(String username){ String f = username.substring(0,1); String l = username.substring(username.length()-1); String str = l+f+username+l+f; String token = username+"."+Base64.encode(str.getBytes()); return token; } }
2.3 前端獲取並存儲token(cookie,localstorage)
-
在登錄頁面的回調函數中
-
if(res.data.code==0){ var token = res.data.data; //在前端存儲token document.cookie = "token="+token; //localStorage.setItem("token",token); //跳轉到index.html location.href = "index.html"; }else{ //彈出提示框:提示登錄失敗 }
2.4 當前端再次請求服務器接口時必須攜帶token
-
當再次請求接口時,需要攜帶token
-
//【獲取token】 //1.從cookie獲取 var cks = document.cookie.split(","); for(var i=0; i<cks.length; i++){ if(cks[i].split("=")[0] == "token"){ this.token = cks[i].split("=")[1]; } } //2.從localStorage獲取 this.token = localStorage.getItem("token"); //【axios發送請求,header攜帶token】 //1.設置axios的請求header中常駐token axios.defaults.headers.common["token"] = this.token; //2.在每次請求時設置token axios({ url:"", method:"", params:{}, data:{}, headers:{ token:this.token } }).then(res=>{ //處理響應結果 });
2.5 在服務器通過攔截器驗證token
-
-
public class UnTokenException extends Exception { public UnTokenException(String message) { super(message); } }
- 創建攔截器
-
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if("options".equalsIgnoreCase(request.getMethod())){ return true; }else { String token = request.getHeader("token"); System.out.println(token); if (token != null && !"".equals(token)) { //驗證token String username = token.split("[.]")[0]; String rightToken = TokenUtil.createToken(username); if (rightToken.equals(token)) { return true; } else { //拋出自定義異常,通過全局異常處理返回給前端一個VO(包含“請先登錄”提示信息) throw new UnTokenException("非法token,請認證"); } } else { throw new UnTokenException("請求參數不正確(缺少token)"); } } } }
-
配置攔截器
-
@Configuration public class AppConfig implements WebMvcConfigurer { @Resource private LoginInterceptor loginInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInterceptor) .addPathPatterns("/**") .excludePathPatterns("/user/**"); } }
- 通過全局異常處理,處理token不合法問題
-
@ControllerAdvice @CrossOrigin public class UnTokenExceptionHanlder { @ExceptionHandler @ResponseBody public ResultVO exceptionHanlder(Exception e){ return new ResultVO(1,e.getMessage()); } }
三、服務器跨域設置
-
跨域配置
-
@Configuration public class CrossConfig { @Bean public WebMvcConfigurer getWebMvcConfigurer() { WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedHeaders("*") .allowedMethods("*") .allowCredentials(false); } }; return webMvcConfigurer; } }
-
-
-
-
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if("options".equalsIgnoreCase(request.getMethod())){ return true; }else { String token = request.getHeader("token"); System.out.println(token); if (token != null && !"".equals(token)) { //驗證token String username = token.split("[.]")[0]; String rightToken = TokenUtil.createToken(username); if (rightToken.equals(token)) { return true; } else { //拋出自定義異常,通過全局異常處理返回給前端一個VO(包含“請先登錄”提示信息) throw new UnTokenException("非法token,請認證"); } } else { throw new UnTokenException("請求參數不正確(缺少token)"); } } } }
-
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.8.3</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency>
String token = Jwts.builder() .setSubject(user.getUsername()) //設置用戶信息 .setId(user.getId()+"") //設置用戶ID .setIssuedAt(new Date()) //設置token的創建時間 .setExpiration(new Date(System.currentTimeMillis()+60*1000)) //設置過期時間 .signWith(SignatureAlgorithm.HS256,"qianfeng") //加密方式及key .compact();
@Component public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws UnTokenException, ExpiredJwtException,SignatureException { if("options".equalsIgnoreCase(request.getMethod())){ return true; }else { String token = request.getHeader("token"); System.out.println(token); if (token != null && !"".equals(token)) { //校驗token JwtParser parser = Jwts.parser(); parser.setSigningKey("qianfeng"); //解析token,只要不拋出異常表示token正常 Jws<Claims> claimsJws = parser.parseClaimsJws(token); //從token中獲取信息 Claims body = claimsJws.getBody(); String subject = body.getSubject(); return true; } else { throw new UnTokenException("token為NULL"); } } } }