SpringCloud Gateway + VUE + axios 实现SSO
SSO(Single Sign On)单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,
SSO(Single Sign On)
单点登录(SingleSignOn,SSO),就是通过用户的一次性鉴别登录。当用户在身份认证服务器上登录一次以后,即可获得访问单点登录系统中其他关联系统和应用软件的权限,同时这种实现是不需要管理员对用户的登录状态或其他信息进行修改的,这意味着在多个应用系统中,用户只需一次登录就可以访问所有相互信任的应用系统。这种方式减少了由登录产生的时间消耗,辅助了用户管理,是比较流行的。
摘重点:在多个应用系统中,只需要登录一次,就可以访问其他相互信任的应用系统。
SSO是一种思想,具体的解决方案有很多种,目前常用的解决方案:CAS、JWT、Spring Security
这里我们使用JWT的方式,以下是分析思路
实现思路:
正常流程:用户登录,接口返回token标识 => 将标识存入浏览器全局存储器LocalStorage中 => 在axios请求拦截器中取出token放入请求头 => 网关过滤器判断token正常存在且token有效,不过滤,用户正常访问服务。
不正常流程:用户未登录,或登录失效 => 用户发起请求,请求头中不包含token或token失效 => 网关过滤器进行过滤,中断请求,封装响应体 , 将用户要访问的服务主页进行记录(URI) => axios响应拦截器接收到响应体,获取URI,存入LocalStorage中,并重定向到登录页面 => 登录成功后,判断localStorage中是否存在URI,存在则直接重定向回去,并删除URI。
模拟场景
在SpringCloud这个大生态中,网关作为第一道防线,所有的请求要经过网关的路由转发才能访问到具体的服务,同时我们可以自定义过滤器去请求进行过滤。一下便是利用过滤器实现的单点登录。
=======================分割线=======================
网关过滤器代码
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//1.获取请求头中参数token
String token = exchange.getRequest().getHeaders().getFirst("token");
log.info("token:{}",token);
//2.判断请求头中是否存在token
if(StringUtils.isBlank(token) || (jwtUtils.getTokenClaim(token) == null)) {
log.info("*********token为null,用户未登录");
//请求路径
String requestPath = exchange.getRequest().getPath().toString();
System.out.println("请求路径:" + requestPath);
//判断是否在进行登录操作
if (requestPath.startsWith(USER_REQUEST)) {
log.info("*********没有登录,但在进行登录相关操作,放行");
return chain.filter(exchange);
}
//静态常量管理
String uri = "";
//根据用户发起请求,判断所在服务的域名
if (requestPath.startsWith(NEWS_REQUEST)) {
uri = NEWS_INDEX;
} else if (requestPath.startsWith(PRODUCT_REQUEST)) {
uri = PRODUCT_INDEX;
}
//下面设置返回体的具体内容
//在lambda表达式中使用的变量应该是final或有效的final,uri是用户原始访问服务的首页地址
final String finalUri = uri;
return Mono.defer(() -> {
//响应对象
final ServerHttpResponse response = exchange.getResponse();
// **:状态码的大小会影响到是否到被 响应拦截器拦截 这里设置状态码为203,代表没有授权信息
response.setStatusCode(HttpStatus.NON_AUTHORITATIVE_INFORMATION);
//响应数据
byte[] bytes = "{\"code\":\"99000\",\"message\":\"Can't access,No permission\"}".getBytes(StandardCharsets.UTF_8);
//封装响应信息
DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
//响应头中放入参数uri,留作登陆成功后的页面跳转
response.getHeaders().set("originUri", finalUri);
// ** : 上一条语句可以让我们在浏览器查看响应时看到对应参数,但是想要获取到参数值,我们需要有下边这条
response.getHeaders().add("Access-Control-Expose-Headers", "originUri");
return response.writeWith(Flux.just(buffer));//设置body
});
}
//鉴权通过,放行
return chain.filter(exchange);
}
Axios相应拦截器代码
axios.interceptors.response.use(function (response) {
// ** : 状态码2开头的才会被这个响应拦截器拦截
console.log("response",response);
//状态码 203 代表没有授权信息
if (203 === response.status) {
//将响应头中的uri存入localStorage中,登录成功后完成跳转
localStorage.setItem("uri",response.headers.originuri)
//跳转到登录页面
router.push("/");
}
return response;
}, function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
});
更多推荐
所有评论(0)