我之前写过一篇用shiro实现登录认证的博文,今天就是在这基础上做出修改而成。由于对shiro认识不够深入,折腾了很久,今天主要就是对遇到的问题做出点小总结。
首先我先给出shiro的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--web.xml中shiro的filter对应的bean-->
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证-->
<property name="loginUrl" value="/login"/>
<property name="successUrl" value="/homepage"/>
<!--自定义的filter-->
<property name="filters">
<map>
<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 -->
<entry key="authc" value-ref="formAuthenticationFilter"/>
</map>
</property>
<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
<!--对静态资源设置匿名访问-->
/images/**=anon
/js/**=anon
/style/**=anon
/resources/**=anon
/kaptcha**=anon
/homepage=anon
<!-- -/**=authc 表示所有的url都必须认证通过才可以访问- -->
/**=authc
</value>
</property>
</bean>
<!--securityManage-->
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm"/>
<!--注入会话管理器-->
<property name="sessionManager" ref="sessionManager"/>
</bean>
<!--自定义realm-->
<bean id="customRealm" class="shiro.shirorealm.CustomRealm">
<!--将凭证匹配器设置到我们自定义realm的配置中-->
<property name="credentialsMatcher" ref="credentialsMatcher"/>
</bean>
<!--数据库中存储到的md5的散列值,在realm中需要设置数据库中的散列值它使用散列算法及散列次数,
让shiro进行散列对比时和原始数据库中的散列值使用的算法一致-->
<!-- (密码)凭证匹配器 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5" />
</bean>
<!--会话管理器-->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="sessionIdUrlRewritingEnabled" value="false" />
<!-- session的失效时长,单位毫秒 -->
<property name="globalSessionTimeout" value="600000"/>
<!-- 删除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
</bean>
<!--自定义的from认证过滤器-->
<bean id="formAuthenticationFilter" class="filter.CustomFromAuthenticationFilter">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="username"/>
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password"/>
</bean>
</beans>
在这要注意的是,过滤器链,而我遇到的问题也正是过滤器链所导致的。
问题一:我集成了kaptcha来实现验证码,但是发现最后到页面的时候,发现当我点击换一张的时候就不能显示了。
原因:原因是因为我在过滤器链那设置验证码是可匿名访问,但是生成验证码也有个controller,所以有个url。最初我是这样设置的:“/kaptcha=anon”,这就造成了当我点击换一张的时候,找不到路径所以不显示。这还有个原因是在我设置了更新验证码的方法那。为了不让验证码缓存,所以在每次更新的时候都会在/kaptcha路径后面加个时间:/kaptcha?=new Date().getTime();这就导致路径发生了变化。
解决方法:将/kaptcha=anon改成/kaptcha**=anon,让它匹配以“/kaptcha”开头的验证码链接。
问题二:当我验证成功后想跳转到成功页面,但是报404错误,老是跳转到xxx.ico
疑惑:我在shiro的配置文件中已经加入了成功后跳转的设置l<property name="successUrl" value="/homepage"/> ,但就是不行。
原因:原因是successUrl的配置只是作为一种附加的配置,只有session中没有用户请求地址的时候才会使用successUrl。
解决方法:由于我是使用restful风格来拦截,所以这时应该复写FormAuthenticationFilter的onLoginSuccess()方法。代码如下:
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
WebUtils.issueRedirect(request,response,getSuccessUrl());
return false;
}
重点:重写shiro的表单过滤器来加入验证码验证的逻辑。关于自定义filter的配置,上面的shiro的配置文件都写上了
public class CustomFromAuthenticationFilter extends FormAuthenticationFilter{
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest= (HttpServletRequest) request;
HttpSession session=httpServletRequest.getSession();
String verifyCode;
try{
verifyCode=httpServletRequest.getParameter("verifyCode").toUpperCase();
}catch (NullPointerException e){
verifyCode=null;
}
//判断验证码输入是否正确
if(verifyCode!=null && !verifyCode.equals(session.getAttribute(Constants.KAPTCHA_SESSION_KEY))){
//如果校验失败,将验证码错误的失败信息,通过shiroLoginFailure设置到request中
httpServletRequest.setAttribute("shiroLoginFailure","randomCodeError");
//拒绝访问,不再校验账号和密码
return true;
}
return super.onAccessDenied(request, response);
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
WebUtils.issueRedirect(request,response,getSuccessUrl());
return false;
}
}