spring security3.1 实现验证码自定义登录
spring security 自定义登录:
1、自定义表结构
T_USER:(用户表)
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar,
`password` varchar
?
T_ROLE:(角色表)
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`role` varchar--角色名(spring security定义角色名以ROLE_开头)
?
T_USER_ROLE:(用户角色关联表)
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`user_id` bigint(11),
`role_id` bigint(11)
?
2、需要的jar包:
spring-security-acl-3.1.0.RC2.jar
spring-security-aspects-3.1.0.RC2.jar
spring-security-cas-3.1.0.RC2.jar
spring-security-config-3.1.0.RC2.jar
spring-security-core-3.1.0.RC2.jar
spring-security-ldap-3.1.0.RC2.jar
spring-security-openid-3.1.0.RC2.jar
spring-security-taglibs-3.1.0.RC2.jar
spring-security-web-3.1.0.RC2.jar
?
3、登录页面login.jsp
<form id="loginForm" action="<c:url value='/j_spring_security_check'/>" method="post"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> <th>用户名:</th> <td><input type="text" name="j_username" id="username" /> </td> <td rowspan="3"><a onclick="javascript:login();"><img src="../img/home/login_btn.gif" width="111" height="99" /> </a></td> </tr> <tr> <th>密 码:</th> <td><input type="password" name="j_password" id="password" /></td> </tr> <tr> <th>验证码:</th> <td><input type="text" maxlength="4" name="code" id="code" style="width: 80px;" onkeypress="javascript:pressKey(event);" /> <img id="validateCode" src="validate.code" width="75" height="33" /> <a onclick="javascript:reload();"><img src="../img/home/refresh.gif" width="22" height="22" /> </a> </td> </tr> </table> </form> <c:if test="${not empty param.error}"> <font color="red"> 登录失败<br /> <br /> 原因: <c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" /></font> </c:if>?
?
?
4、application-security.xml 配置
?
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:b="http://www.springframework.org/schema/beans" xmlns:beans="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-3.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> <http> <!-- 不拦截 --> <intercept-url pattern="/login.jsp*" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <intercept-url pattern="/list/**" access="ROLE_ADMIN,ROLE_MANAGER,ROLE_USER" /> <intercept-url pattern="/delete/**" access="ROLE_ADMIN" /> <!-- 设置登录过滤器 --> <custom-filter before="FORM_LOGIN_FILTER" ref="validateCodeAuthenticationFilter" /> <!-- 登录表单设置 --> <form-login login-page="/home/login" default-target-url="/home/loginSuccess.action"(登录成功的url) authentication-failure-url="/home/login.action?error=true"(登录失败的url) /> <!-- 登出操作后跳转到该页面 --> <!-- <logout logout-success-url="/loggedout.jsp" delete-cookies="JSESSIONID" /> <remember-me /> --> <!-- SESSION超时后跳转到该页面 --> <!-- <session-management invalid-session-url="/timeout.jsp"> </session-management> --> </http> <authentication-manager alias="authenticationManager"> <authentication-provider> <!-- 直接使用SQL语句查询登录帐号对应权限, users-by-username-query:查询登录用户是否存在 authorities-by-username-query:查询登录用户权限(登录用户可以不属于任何组,从t_user_role表中获取权限) group-authorities-by-username-query:查询登录用户所在组的权限 --> <jdbc-user-service data-source-ref="webDataSource" users-by-username-query="SELECT t_user.name AS username,t_user.password as password,'true' AS enabled FROM t_user WHERE t_user.name = ?" authorities-by-username-query="SELECT name AS username,role as authorities FROM T_USER LEFT OUTER JOIN t_role AS userrole ON(t_user.id = userrole.user_id) LEFT OUTER JOIN t_role AS role ON (userrole.role_id = role.id) WHERE t_user.name = ?" /> </authentication-provider> </authentication-manager> <!-- 验证码过滤器 --> <beans:bean id="validateCodeAuthenticationFilter" ref="authenticationManager"></beans:property> </beans:bean> <!-- 登录成功 --> <beans:bean id="loginLogAuthenticationSuccessHandler" value="/home/loginSuccess.action"></beans:property> </beans:bean> <!-- 登录失败 --> <beans:bean id="simpleUrlAuthenticationFailureHandler" value="/home/login.action?error=true"></beans:property> </beans:bean></beans:beans>
?
5、验证码验证
package cn.com.trade.security;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import org.apache.commons.lang3.StringUtils;import org.springframework.security.authentication.AuthenticationServiceException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.util.TextEscapeUtils;import cn.com.fxark.util.md5.MD5;import cn.com.trade.domain.adminuser.AdminUser;import cn.com.trade.global.GlobalParameter;import cn.com.trade.service.ServiceManager;/** * 带验证码校验功能的用户名、密码认证过滤器 * * 支持不输入验证码;支持验证码忽略大小写。 * * @author Long * */public class ValidateCodeUsernamePasswordAuthenticationFilter extendsUsernamePasswordAuthenticationFilter {private boolean postOnly = true;private boolean allowEmptyValidateCode = false;private String sessionvalidateCodeField = DEFAULT_SESSION_VALIDATE_CODE_FIELD;private String validateCodeParameter = DEFAULT_VALIDATE_CODE_PARAMETER;public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME";// session中保存的验证码public static final String DEFAULT_SESSION_VALIDATE_CODE_FIELD = "rand";// 输入的验证码public static final String DEFAULT_VALIDATE_CODE_PARAMETER = "code";@Overridepublic Authentication attemptAuthentication(HttpServletRequest request,HttpServletResponse response) throws AuthenticationException {if (postOnly && !request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: "+ request.getMethod());}String username = obtainUsername(request);String password = obtainPassword(request);if (username == null) {username = "";}if (password == null) {password = "";}username = username.trim();UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);// Place the last username attempted into HttpSession for viewsHttpSession session = request.getSession(false);if (session != null || getAllowSessionCreation()) {request.getSession().setAttribute(SPRING_SECURITY_LAST_USERNAME_KEY,TextEscapeUtils.escapeEntities(username));}// Allow subclasses to set the "details" propertysetDetails(request, authRequest);// check validate codeif (!isAllowEmptyValidateCode())checkValidateCode(request);// 根据用户和密码查询AdminUser adminUser = ServiceManager.getInstance().getAdminUserService().login(username, MD5.getMD5ofStr(password));session.setAttribute(GlobalParameter.HT_ADMIN_LOGIN_USER_SESSION_NAME,adminUser);return this.getAuthenticationManager().authenticate(authRequest);}/** * * <li>比较session中的验证码和用户输入的验证码是否相等</li> * */protected void checkValidateCode(HttpServletRequest request) {String sessionValidateCode = obtainSessionValidateCode(request);String validateCodeParameter = obtainValidateCodeParameter(request);if (StringUtils.isEmpty(validateCodeParameter)|| !sessionValidateCode.equalsIgnoreCase(validateCodeParameter)) {throw new AuthenticationServiceException("验证码错误!");}}private String obtainValidateCodeParameter(HttpServletRequest request) {return request.getParameter(validateCodeParameter);}protected String obtainSessionValidateCode(HttpServletRequest request) {Object obj = request.getSession().getAttribute(sessionvalidateCodeField);return null == obj ? "" : obj.toString();}public boolean isPostOnly() {return postOnly;}@Overridepublic void setPostOnly(boolean postOnly) {this.postOnly = postOnly;}public String getValidateCodeName() {return sessionvalidateCodeField;}public void setValidateCodeName(String validateCodeName) {this.sessionvalidateCodeField = validateCodeName;}public boolean isAllowEmptyValidateCode() {return allowEmptyValidateCode;}public void setAllowEmptyValidateCode(boolean allowEmptyValidateCode) {this.allowEmptyValidateCode = allowEmptyValidateCode;}}?
?
<?xml version="1.0" encoding="UTF-8"?><beans:beans xmlns="http://www.springframework.org/schema/security"xmlns:beans="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"default-autowire="byName" default-lazy-init="false"><intercept-url pattern="/netservice/*" access="IS_AUTHENTICATED_ANONYMOUSLY" /><global-method-security pre-post-annotations="enabled"></global-method-security> <http use-expressions="true" auto-config="true" access-denied-page="/timeOut.jsp"><form-login login-page="/login.jsp" always-use-default-target="true"default-target-url="/index.jsp"authentication-failure-url="/login.jsp?error=1"/> <logout logout-success-url="/index.jsp" /><!-- 实现免登陆验证 --> <remember-me /> <session-management invalid-session-url="/timeout.jsp"> <concurrency-control max-sessions="10" error-if-maximum-exceeded="true" /> </session-management> <!-- 增加一个filter,这点与Acegi是不一样的,不能修改默认的filter了, 这个filter位于FILTER_SECURITY_INTERCEPTOR之前 --><custom-filter ref="myFilter"before="FILTER_SECURITY_INTERCEPTOR" /></http> <!-- 一个自定义的filter,必须包含authenticationManager,accessDecisionManager,securityMetadataSource三个属性, 我们的所有控制将在这三个类中实现,解释详见具体配置 --><beans:bean id="myFilter"/></beans:bean><beans:bean id="accessDecisionManager"class="com.health.system.springSecurity.MyAccessDecisionManager"></beans:bean><beans:bean id="filterInvocationSecurityMetadataSource"class="com.health.system.springSecurity.MyInvocationSecurityMetadataSource"></beans:bean> <!-- 验证配置 , 认证管理器,实现用户认证的入口,主要实现UserDetailsService接口即可 --><authentication-manager alias="authenticationManager"><authentication-provideruser-service-ref="userDetailServiceImpl"><password-encoder ref="passwordEncoder"></password-encoder></authentication-provider></authentication-manager><beans:bean id="userDetailServiceImpl"class="com.health.system.springSecurity.UserDetailServiceImpl"></beans:bean><beans:bean id="passwordEncoder"class="com.health.system.springSecurity.MyPasswordEncoder"></beans:bean><!-- 获取spring上下文 --><beans:bean id="SpringContextUtil"class="com.health.system.javacommon.util.SpringContextUtil"></beans:bean></beans:beans>