Springboot集成shiro,登录重定向跨域问题

????集成方式

项目采用了前后端分离的结构,前端使用的是vue框架,后端使用的是springboot框架,登录认证框架是shiro,同时使用了单点登录。

整个登录认证的逻辑:

  • 通过shiro判断用户是否登陆了
  • 如果没有登录或者登录过期,通过shiro的配置跳转到指定的controller
  • controller中重定向到单点登录页面

????问题描述

  • 当登录过期了前端先发送一个请求到接口A
  • shiro判断了用户已经登录过期了,返回状态码302,并让前端重新请求到接口B
  • 在接口B,返回状态码302,并让前端重新请求到单点登录地址
  • 前端重新请求这个单点登录地址
  • 浏览器判断单点登录地址和当前系统地址不是同一个,于是就出现跨域错误

????解决思路

这个问题在网上找了很多解决的办法都是通过解决跨域,比如使用注解@CrossOrigin,但是都没有生效。

后来思考了下,跨域是重定向和父页面不同的地址出现的,这个时候在后端怎么配置其实已经没有用了。

现在核心问题,当登录失效之后就不返回状态码302,而是直接调用退出登录接口,这样也能达到重新登录的目的。

????源码分析

为了解决这个问题,我们需要先研究一下shiro的源码如何来判断用户登录过期的,并且返回302重定向到新接口的。

在这里插入图片描述

通过上面的时序图可以看出最后调用的是AccesssControlFilter类的onPreHandle方法,这个方法调用了两个方法:isAccessAllowedonAccessDenied

isAccessAllowed:判断用户是否登录,如果登陆了直接返回了,如果没有登录就会执行第二个方法onAccessDenied:是否拒绝登录,在这里面就可以自定义一些返回值

public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
    return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}

从这里可以看出,如果我们想自己定义返回值的话就要重写这两个方法。

????解决办法

  • 首先定义一个Filter,这个Filter继承AuthorizationFilter,然后重写了isAccessAllowedonAccessDenied

    onAccessDeniedResponse就返回401,前端拿到这个401就请求退出接口

public class RefreshAuthorizationFilter extends AuthorizationFilter {

    private ISecurityService securityService;

    public RefreshAuthorizationFilter(ISecurityService securityService) {
        this.securityService = securityService;
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return securityService.isLogin();
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        response.getWriter().write("401");
        return false;
    }
}
  • 使用配置将Filter加入Filter链中,并且加上@Primary注解,这样优先级才会优先执行。
@Primary
@Component
public class RefreshALoginConfig extends ALoginConfig {

    @Autowired
    private ISecurityService securityService;

    @Override
    protected void addFilters() {
        addFilter("restartRefresh", new RefreshAuthorizationFilter(securityService));
    }

    protected void initFilterChainDefinitions() {
        filter("/api/**", "restartRefresh");
    }
}

上一篇:【Python】OPC UA 服务器与客户端的实现-OPC UA客户端


下一篇:Flutter 使用flutter_swiper_null_safety 实现轮播图