SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题

最近在项目上线的时候遇到了个问题,就是SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题。返回的结果是302。

错误的原因是,在https的环境内,限制http,而在Shiro框架内置的用户登陆校验,在校验失败后会保存当前请求,然后重定向到登陆页面(因为是前后端分离,我这里跳转的登录页直接就是401的JSON,通知前端进行跳转)。Shiro内部重定向的请求是http,所以请求异常。具体源码在下面:

FormAuthenticationFilter:

SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题_第1张图片

AccessControlFilter:

SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题_第2张图片

具体是不是这个兼容HTTP1.0的默认值的问题,还不好说,因为重写这个方法改为false也没有效果。

SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题_第3张图片

 所以,下面提供了两个解决方案:

这里因为是前后端分离,所以原本就有一个解决涉及到预请求OPTIONS没有权限影响到跨域的过滤器。所以就直接在这个过滤器上进行改动了,如果没有的,原本没有过滤器的可以添加一个。

通过重写onAccessDenied方法。

解决方案1:直接抛出异常,让我们全局的异常捕捉,返回登陆提示。

解决方案2:直接返回登陆提示。不进行后续的转发操作。

public class CorsAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean allowed = super.isAccessAllowed(request, response, mappedValue);
        // 提示OPTIONS预检时,后端需要设置的两个常用自定义头
        HttpServletResponse httpServletResponse = (HttpServletResponse)response;
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
        if (!allowed) {
            // 判断请求是否是options请求
            String method = WebUtils.toHttp(request).getMethod();
            if (StringUtils.equalsIgnoreCase("OPTIONS", method)) {
                return true;
            }
        }
        return allowed;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (isLoginRequest(request, response)) {
            if (isLoginSubmission(request, response)) {
                return executeLogin(request, response);
            } else {
                return true;
            }
        } else {
            //解决方案1:直接抛出异常,异常会被捕获处理
            //throw new UnauthenticatedException();
            //解决方案2:在这里进行返回,不抛出异常,也不进行跳转
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json");
            //获取会话
            Subject subject = getSubject(request, response);
            if (subject.getPrincipal() == null) {
                //写回给客户端
                PrintWriter out = response.getWriter();
                out.write(JSONUtil.objectToJson(RestStatus.createByError(ResultCodeEnum.INVALID_USER)));
                //刷新和关闭输出流
                out.flush();
                out.close();
            }
            return false;
        }
    }
}

然后配置下这个过滤器,这个过滤器,只作用于需要登陆的接口。

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")         DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 修改调整的登录页面
        shiroFilterFactoryBean.setLoginUrl("/page/401");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/page/index");
        // 未授权提示页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/page/403");
        Map filters = shiroFilterFactoryBean.getFilters();
        filters.put("shiroCorsFilter", new CorsAuthenticationFilter());
        //拦截器
        Map filterChainDefinitionMap = new LinkedHashMap<>();
        //带api路由的所有请求全部要求登陆
        filterChainDefinitionMap.put("/api/**", "shiroCorsFilter");
        //其他的所有请求放行
        filterChainDefinitionMap.put("/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

这样就解决了问题。如果能给诸位带来帮助,麻烦点个赞。

你可能感兴趣的:(shiro框架,springboot,shiro,servlet,java,前端)