SpringCloud Gateway + VUE + axios 实现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);
});
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

To Do.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值