實現不復雜,但比較繁瑣,建議結合項目查看
首先,我們的登錄驗證基於網關服務,如果網關沒有檢測到cookie中由jti或者沒有找到對應的jwt,我們認為用戶沒有登錄
此時要跳轉到登錄頁面
網關是基於過濾器實現的,過濾器為
@Component public class AuthFilter implements GlobalFilter, Ordered { @Autowired private AuthService authService;
//定義跳往登錄頁面的controller private static final String LOGIN_URL ="http://localhost:8001/api/oauth/toLogin"; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //過濾調用業務的請求,判斷現在是否已經登錄(cookie中有沒有jti),如果以登錄,向請求頭中加入jwt令牌 ServerHttpResponse response = exchange.getResponse(); ServerHttpRequest request = exchange.getRequest(); //判斷當前的請求路徑是否是登錄請求,如果是,直接放行 String path = request.getURI().getPath(); if("/api/oauth/login".equals(path)||! UrlFilter.hasAuthorize(path)){ //直接放行 return chain.filter(exchange); } //從cookie中獲取jti,如果jti不存在,拒絕訪問 String jti = authService.getJtiFromCookie(request); if(StringUtils.isEmpty(jti)){ //如果jti為空,拒絕訪問 // response.setStatusCode(HttpStatus.UNAUTHORIZED); // return response.setComplete(); //如果jti為空,跳轉登錄頁面 from就是當前的網頁地址,待會還要跳回來 return this.toLoginPage(LOGIN_URL+"?FROM="+request.getURI().getPath(),exchange); } //從redis中獲取jwt令牌,如果該值不存在,拒絕本次訪問 String jwt = authService.getJwtFromRedis(jti); if(StringUtils.isEmpty(jwt)){ //如果jwt為空,拒絕訪問 // response.setStatusCode(HttpStatus.UNAUTHORIZED); // return response.setComplete(); //如果jwt為空,跳轉到登錄頁面 from就是當前的網頁地址,待會還要跳回來 return this.toLoginPage(LOGIN_URL?FROM="+request.getURI().getPath(),exchange); } //對當前的請求對象進行增強,讓他攜帶令牌信息,因為微服務里如果引入了oauth2,會對令牌進行校驗 request.mutate().header("Authorization","Bearer "+jwt); System.out.println("令牌為"+jwt); return chain.filter(exchange); } //跳轉到的登陸頁面 private Mono<Void> toLoginPage(String loginUrl, ServerWebExchange exchange) { ServerHttpResponse response = exchange.getResponse(); response.setStatusCode(HttpStatus.SEE_OTHER); //這個狀態碼的意思是跳轉 response.getHeaders().set("Location",loginUrl); return response.setComplete(); } @Override public int getOrder() { return 0; } }
網關發請求到跳轉controller,這是controller
@Controller @RequestMapping("/oauth") public class AuthController { @Value("${auth.clientId}") private String clientId; @Value("${auth.clientSecret}") private String clientSecret; @Value("${auth.cookieDomain}") private String cookieDomain; //值為localhost @Value("${auth.cookieMaxAge}") private int cookieMaxAge; //值為-1 @Autowired private AuthService authService; @RequestMapping("/login") @ResponseBody public Result login(String username, String password, HttpServletResponse response){ //校驗相關的參數 if(StringUtils.isEmpty(username)){ throw new RuntimeException("請輸入用戶名"); } if(StringUtils.isEmpty(password)){ throw new RuntimeException("請輸入密碼"); } //申請令牌 AuthToken authToken = authService.login(username, password, clientId, clientSecret); //將jti的值存入cookie中 this.saveJtiToCookie(authToken.getJti(),response); //返回結果 return new Result(true, StatusCode.OK,"登錄成功",authToken.getJti()); } //將jwt令牌的短標識jti存到cookie中 private void saveJtiToCookie(String jti, HttpServletResponse response) { CookieUtil.addCookie(response,cookieDomain,"/","uid",jti,cookieMaxAge,false);//httpOnly=true時,將無法通過js讀取cookie,有利於防止攻擊 } //跳轉到登錄頁面 @RequestMapping("/toLogin") //參數用於跳回登陸前的頁面,required表示非必須 public String toLogin(@RequestParam(value = "FROM",required = false,defaultValue = "") String from, Model model){ model.addAttribute("from",from); return "login";//這是指login.html文件 } }
在login.html中的js中,定義參數接收model中的from,並定義登錄成功后自動跳轉