需求:实现验证码功能
shiro使用FormAuthenticationFilter进行表单认证,验证校验的功能应该写在FormAuthenticationFilter中
在认证(验证账号和密码)之前进行验证码校验。
需要写FormAuthenticationFilter的子类,继承FormAuthenticationFilter,改写它的认证方法,在认证之前进行验证码校验
1.sessionManager
和shiro整合后,使用shiro的session管理,shiro提供sessionDao操作 会话数据
配置sessionManager
<!-- securityManager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- 将realm注入到当前的securityManager中 -->
<property name="realm" ref="customRealm"></property>
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"></property>
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager"></property>
</bean>
<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的失效时长,单位为毫秒 -->
<property name="globalSessionTimeout" value="600000"></property>
<!-- 删除失效的session -->
<property name="deleteInvalidSessions" value="true"></property>
</bean>
2.自定义FormAuthenticationFilter
//自定义的表达认证器要继承FormAuthenticationFilter
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter{
//原FormAuthenticationFilter的认证方法
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
//在这里进行验证码校验
//在session获取正确验证码
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session = httpServletRequest.getSession();
//在session中取出正确的验证码
String validateCode = (String)session.getAttribute("validateCode");
//取出用户输入的验证码
String randomcode = httpServletRequest.getParameter("randomcode");
//进行校验
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校验失败,将验证码错误失败的信息,通过shiroLoginFailure设置到request
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒绝访问
return true;
}
//验证码正确,调用父类的onAccessDenied方法进行username和password的校验
return super.onAccessDenied(request, response);
}
}
3.配置自定义的FormAuthenticationFilter
在shiro的过滤器中配置,applicationContext-shiro.xml
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 配置安全管理器 -->
<property name="securityManager" ref="securityManager"></property>
<!--
如果账户还没有认证就进行请求,当请求访问的资源不是匿名的话,
就会将其请求拦截到下面这个url中 ,进行登录认证
-->
<property name="loginUrl" value="/login.action"></property>
<!-- 这是认证之后访问没有权限的资源,会统一跳转到这个jsp -->
<property name="unauthorizedUrl" value="/refuse.jsp"></property>
<!-- 自定义filter配置 -->
<property name="filters">
<map>
<!-- 将自定义的FormAuthenticationFilter注入shiroFilter中 -->
<entry key="authc" value-ref="formAuthenticationFilter"></entry>
</map>
</property>
<!-- 配置自定义form认证过滤器 -->
<bean id="formAuthenticationFilter" class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="username"></property>
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password"></property>
</bean>
4.在login.action对验证错误 进行解析
@RequestMapping("login")
public String login(HttpServletRequest request) throws Exception{
/**
* 如果登录失败从request中获取认证的异常信息,
* shiroLoginFailure就是shiro异常类的全限类名,
* 可以获取异常信息,其实抛出异常信息就是抛出异常信息的类名
*/
String exceptionClassName = request.getParameter("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定的异常信息
if(exceptionClassName!=null){
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
throw new CustomException("账号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new CustomException("用户名/密码错误");
} else if("randomCodeError".equals(exceptionClassName)){
throw new CustomException("验证码错误");
} else{
throw new Exception();//最终在异常处理器生成未知错误
}
}
/**
* 这个方法-login(),这个方法是处理认证不成功的
* 认证是交给shiro处理的,在applicationContext-shiro.xml中配置拦截器
* 当请求的url为:login.action的时候,它首先是不会进入这个方法,而是被shiro的拦截器给拦住了,
* 进入了shiro的认证机制,shiro最终会调用自定义的realm进行认证,
* 通过用户输入的与数据库的比对,如果不正常的话,这时候才会进入到这个方法,其中抛出的异常信息
* 可以在request中获取,通过request.getParameter("shiroLoginFailure")可以获取
*
* shiro认证成功会自动跳转到上一个请求的路径
* 登录失败跳转到login页面
*
*/
return "login";
}
5.在登陆页面添加验证码
<TR>
<TD>验证码:</TD>
<TD><input id="randomcode" name="randomcode" size="8" /> <img
id="randomcode_img" src="${baseurl}validatecode.jsp" alt=""
width="56" height="20" align='absMiddle' /> <a
href=javascript:randomcode_refresh()>刷新</a></TD>
</TR>
6.在filter配置匿名访问验证码jsp
<!-- 验证码,可匿名访问 -->
/validatecode.jsp = anon