最近项目上线跳转登录页面的时候报错400,权限框架使用的是shiro,排查后发现是https跳转登录页面变成了http导致的问题。
查看源码可以发现,shiro的登录过滤器FormAuthenticationFilter的方法中调用了saveRequestAndRedirectToLogin方法
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
saveRequest(request);
redirectToLogin(request, response);
}
再进到redirectToLogin方法
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
String loginUrl = getLoginUrl();
WebUtils.issueRedirect(request, response, loginUrl);
}
可以看到调用了WebUtils的issueRedirct方法,再进入到issueRedirct方法,会发现实际上是调用了同名方法如下,且http10Compatible参数默认传了ture
public static void issueRedirect(ServletRequest request, ServletResponse response, String url, Map queryParams, boolean contextRelative, boolean http10Compatible) throws IOException {
RedirectView view = new RedirectView(url, contextRelative, http10Compatible);
view.renderMergedOutputModel(queryParams, toHttp(request), toHttp(response));
}
到这里为止,我们就知道了,要想实现https跳转https,只需要想办法把http10Compatible改为false就行了。这里我们自己实现一个shiro的登录过滤器重写onAccessDenied方法就可以,代码如下:
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {
@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 {
saveRequestAndRedirectToLogin(request, response);
return false;
}
}
protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
saveRequest(request);
redirectToLogin(request, response);
}
protected void redirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
String loginUrl = getLoginUrl();
WebUtils.issueRedirect(request, response, loginUrl, null, true, false);
}
}
自定义登录过滤器写好后,要在配置类或配置文件中将它配置到shiroFilter中:
配置文件的写法:
<!-- 自定义的登录过滤器-->
<bean id="customFilter" class="com.senzhuang.shiro.CustomFormAuthenticationFilter" />
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.html"></property>
<property name="unauthorizedUrl" value="/refuse.html"></property>
<property name="filters">
<map>
<entry key="authc" value-ref="customFilter"/>
</map>
</property>
</bean>
配置类的写法:
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login.html");
shiroFilter.setUnauthorizedUrl("/refuse.html");
Map<String, Filter> map = new HashMap<String, Filter>();
map.put("authc", new CustomFormAuthenticationFilter());
shiroFilter.setFilters(map);
return shiroFilter;
}
这样就能完美解决https跳转http的问题。