Shiro认证框架下,加入图形验证码

来源:互联网 发布:damn it 编辑:程序博客网 时间:2024/05/21 14:47


目录结构:

  1. 概述创建验证码异常类:CaptchaException.java
  2. 扩展shiro认证
  3. 验证码工具
  4. 验证码servlet
  5. 配置文件修改
  6. 修改登录页面
  7. 测试验证

[一]、概述

本文简单讲述在web应用整合shiro后,如何实现登录验证码认证的功能。

[二]、扩展shiro的认证

创建验证码异常类:CaptchaException.java

package com.micmiu.modules.support.shiro;import org.apache.shiro.authc.AuthenticationException;/** * * @author <a href="http://www.micmiu.com">Michael Sun</a> */public class CaptchaException extends AuthenticationException {private static final long serialVersionUID = 1L;public CaptchaException() {super();}public CaptchaException(String message, Throwable cause) {super(message, cause);}public CaptchaException(String message) {super(message);}public CaptchaException(Throwable cause) {super(cause);}}


扩展默认的用户认证的bean为:UsernamePasswordCaptchaToken.java

package com.micmiu.modules.support.shiro;import org.apache.shiro.authc.UsernamePasswordToken;/** * extends UsernamePasswordToken for captcha * * @author <a href="http://www.micmiu.com">Michael Sun</a> */public class UsernamePasswordCaptchaToken extends UsernamePasswordToken {private static final long serialVersionUID = 1L;private String captcha;public String getCaptcha() {return captcha;}public void setCaptcha(String captcha) {this.captcha = captcha;}public UsernamePasswordCaptchaToken() {super();}public UsernamePasswordCaptchaToken(String username, char[] password,boolean rememberMe, String host, String captcha) {super(username, password, rememberMe, host);this.captcha = captcha;}}


扩展原始默认的过滤为:FormAuthenticationCaptchaFilter.java

package com.micmiu.modules.support.shiro;import javax.servlet.ServletRequest;import javax.servlet.ServletResponse;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;import org.apache.shiro.web.util.WebUtils;/** * * @author <a href="http://www.micmiu.com">Michael Sun</a> */public class FormAuthenticationCaptchaFilter extends FormAuthenticationFilter {public static final String DEFAULT_CAPTCHA_PARAM = "captcha";private String captchaParam = DEFAULT_CAPTCHA_PARAM;public String getCaptchaParam() {return captchaParam;}protected String getCaptcha(ServletRequest request) {return WebUtils.getCleanParam(request, getCaptchaParam());}protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) {String username = getUsername(request);String password = getPassword(request);String captcha = getCaptcha(request);boolean rememberMe = isRememberMe(request);String host = getHost(request);return new UsernamePasswordCaptchaToken(username,password.toCharArray(), rememberMe, host, captcha);}}


修改shiro认证逻辑:ShiroDbRealm.java

package com.micmiu.framework.web.v1.system.service;import java.io.Serializable;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AccountException;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.SimpleAuthenticationInfo;import org.apache.shiro.authc.UnknownAccountException;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.authz.SimpleAuthorizationInfo;import org.apache.shiro.cache.Cache;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.subject.SimplePrincipalCollection;import org.springframework.beans.factory.annotation.Autowired;import com.micmiu.framework.web.v1.system.entity.Role;import com.micmiu.framework.web.v1.system.entity.User;import com.micmiu.modules.captcha.CaptchaServlet;import com.micmiu.modules.support.shiro.CaptchaException;import com.micmiu.modules.support.shiro.UsernamePasswordCaptchaToken;/** * 演示用户和权限的认证,使用默认 的SimpleCredentialsMatcher * * @author <a href="http://www.micmiu.com">Michael Sun</a> */public class ShiroDbRealm extends AuthorizingRealm {private UserService userService;/** * 认证回调函数, 登录时调用. */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {UsernamePasswordCaptchaToken token = (UsernamePasswordCaptchaToken) authcToken;String username = token.getUsername();if (username == null) {throw new AccountException("Null usernames are not allowed by this realm.");}// 增加判断验证码逻辑String captcha = token.getCaptcha();String exitCode = (String) SecurityUtils.getSubject().getSession().getAttribute(CaptchaServlet.KEY_CAPTCHA);if (null == captcha || !captcha.equalsIgnoreCase(exitCode)) {throw new CaptchaException("验证码错误");}User user = userService.getUserByLoginName(username);if (null == user) {throw new UnknownAccountException("No account found for user ["+ username + "]");}return new SimpleAuthenticationInfo(new ShiroUser(user.getLoginName(),user.getName()), user.getPassword(), getName());}/** * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用. */@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {ShiroUser shiroUser = (ShiroUser) principals.fromRealm(getName()).iterator().next();User user = userService.getUserByLoginName(shiroUser.getLoginName());if (user != null) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();for (Role role : user.getRoleList()) {// 基于Permission的权限信息info.addStringPermissions(role.getAuthList());}return info;} else {return null;}}/** * 更新用户授权信息缓存. */public void clearCachedAuthorizationInfo(String principal) {SimplePrincipalCollection principals = new SimplePrincipalCollection(principal, getName());clearCachedAuthorizationInfo(principals);}/** * 清除所有用户授权信息缓存. */public void clearAllCachedAuthorizationInfo() {Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();if (cache != null) {for (Object key : cache.keys()) {cache.remove(key);}}}@Autowiredpublic void setUserService(UserService userService) {this.userService = userService;}/** * 自定义Authentication对象,使得Subject除了携带用户的登录名外还可以携带更多信息. */public static class ShiroUser implements Serializable {private static final long serialVersionUID = -1748602382963711884L;private String loginName;private String name;public ShiroUser(String loginName, String name) {this.loginName = loginName;this.name = name;}public String getLoginName() {return loginName;}/** * 本函数输出将作为默认的<shiro:principal/>输出. */@Overridepublic String toString() {return loginName;}public String getName() {return name;}}}


修改web.xml文件,加入

<servlet><servlet-name>kaptcha</servlet-name><servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class></servlet><servlet-mapping><servlet-name>kaptcha</servlet-name><url-pattern>/images/kaptcha.jpg</url-pattern></servlet-mapping>


修改 applicationContext-shiro.xml 中的配置如下:

<!-- Shiro Filter --><bean id="myCaptchaFilter" class="com.micmiu.modules.support.shiro.FormAuthenticationCaptchaFilter"/><bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"><property name="securityManager" ref="securityManager" /><property name="loginUrl" value="/login.do" /><property name="successUrl" value="/index.do" /><property name="filters">    <map>        <entry key="authc" value-ref="myCaptchaFilter"/>    </map></property><property name="filterChainDefinitions"><value>/login.do = authc/logout.do = logout/servlet/* = anon/images/** = anon/js/** = anon/css/** = anon/** = user</value></property></bean>

页面中加入

<div class="field"><label for="captcha" class="field">验证码:</label> <input type="text"id="captcha" name="captcha" size="4" maxlength="4"class="required" /></div><div class="field"><label for="codeImg" class="field"></label> <img title="点击更换" id="img_captcha"onclick="javascript:refreshCaptcha();"src="servlet/captchaCode">(看不清<a href="javascript:void(0)" onclick="javascript:refreshCaptcha()">换一张</a>)</div>


 

<script type="text/javascript">$(document).ready(function() {
var _captcha_id = "#img_captcha";
function refreshCaptcha() {
$(_captcha_id).attr("src","servlet/captchaCode?t=" + Math.random());
}
</script>


 

0 0