最近在项目上线的时候遇到了个问题,就是SpringBoot整合Shiro前后端分离在Https环境下登陆失效的问题。返回的结果是302。
错误的原因是,在https的环境内,限制http,而在Shiro框架内置的用户登陆校验,在校验失败后会保存当前请求,然后重定向到登陆页面(因为是前后端分离,我这里跳转的登录页直接就是401的JSON,通知前端进行跳转)。Shiro内部重定向的请求是http,所以请求异常。具体源码在下面:
FormAuthenticationFilter:

AccessControlFilter:

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

所以,下面提供了两个解决方案:
这里因为是前后端分离,所以原本就有一个解决涉及到预请求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;
- }
这样就解决了问题。如果能给诸位带来帮助,麻烦点个赞。