实现不复杂,但比较繁琐,建议结合项目查看
首先,我们的登录验证基于网关服务,如果网关没有检测到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,并定义登录成功后自动跳转