带图片验证码的用户登录是一个系统中很常见的模块,在Java EE中有很多不同的实现,而开源世界中又给我们提供了很多优秀的框架,从而减少了我们的编码量,下面给大家简单介绍一下用Spring+ Hibernate+JSF+Ajax4jsf+Acegi+jCaptcha 如何构建一个登录模块:
我使用这些框架的版本如下:
Spring 2.0.1
Hibernate 3.2
JSF 1.2
Acegi 1.0.5
jCaptcha 1.0-RC6
首先建表USERTEST,和ROLETEST,作为用户表和角色表
CREATE TABLE USERTEST
(
USERID NUMBER NOT NULL,
USERNAME VARCHAR2(20 CHAR) NOT NULL,
PASSWORD VARCHAR2(100 CHAR) NOT NULL,
STATUS NUMBER DEFAULT
1 NOT NULL,
ROLEID NUMBER NOT NULL
)
CREATE TABLE ROLETEST
(
ROLEID NUMBER NOT NULL,
ROLENAME VARCHAR2(50 CHAR) NOT NULL,
ROLEDESC VARCHAR2(50 CHAR)
)
同时给ROLETEST表添加一条记录:1 ROLE_USER 普通用户
再给USERTEST表添加一条记录:1 admin 123 1 1
对应的hbm.xml文件就相信大家都会写,如果用eclipse开发的话,可以使用HibernateSynchronizer插件,我使用的版本是3.1.9
接下来就要给web.xml的配置一下了:
xmlns="https://www.wendangku.net/doc/691517423.html,/xml/ns/javaee" xmlns:web="https://www.wendangku.net/doc/691517423.html,/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="https://www.wendangku.net/doc/691517423.html,/xml/ns/javaee https://www.wendangku.net/doc/691517423.html,/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> /WEB-INF/classes/applicationContext.xml org.springframework.web.context.ContextLoaderListener org.acegisecurity.ui.session.HttpSessionEventPublisher com.sun.faces.config.ConfigureListener org.springframework.orm.hibernate3.support.OpenSessionInViewFilter org.acegisecurity.util.FilterToBeanProxy org.acegisecurity.util.FilterChainProxy
然后Spring的applicationContext.xml,在这里面你需要配置数据库连接、hibernate 的各种策略、Acegi安全设置、jCaptcha设置等等:
xmlns:xsi="https://www.wendangku.net/doc/691517423.html,/2001/XMLSchema-instance" xmlns:aop="https://www.wendangku.net/doc/691517423.html,/schema/aop" xsi:schemaLocation="https://www.wendangku.net/doc/691517423.html,/schema/beans https://www.wendangku.net/doc/691517423.html,/schema/beans/spring-beans-2.0.xsd https://www.wendangku.net/doc/691517423.html,/schema/aop https://www.wendangku.net/doc/691517423.html,/schema/aop/spring-aop-2.1.xsd"> class="https://www.wendangku.net/doc/691517423.html,mons.dbcp.BasicDataSource"> class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> org.hibernate.dialect.OracleDialect org.hibernate.cache.OSCacheProvider class="org.springframework.orm.hibernate3.HibernateTransactionManager"> class="org.springframework.transaction.interceptor.TransactionProxyFactoryBea n"> class="org.acegisecurity.util.FilterChainProxy"> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /**/*.jsf=httpSessionContextIntegrationFilter,captchaValidationProcessingFi lter,authenticationProcessingFilter,anonymousProcessingFilter,exceptionTranslat ionFilter,filterInvocationInterceptor class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"> com.fifthlab.demo.dao.authentication.FixedCaptchaSecurityContextImpl class="org.acegisecurity.providers.ProviderManager"> class="org.acegisecurity.providers.dao.DaoAuthenticationProvider"> class="org.acegisecurity.event.authentication.LoggerListener" /> class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"> class="org.acegisecurity.ui.ExceptionTranslationFilter"> class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"> class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON PATTERN_TYPE_APACHE_ANT /login.jsp=ROLE_ANONYMOUS /success.jsp=ROLE_USER class="com.fifthlab.demo.dao.authentication.Hibernate3Authentication"> class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"> class="org.acegisecurity.vote.AffirmativeBased"> class="org.acegisecurity.captcha.CaptchaValidationProcessingFilter"> class="com.fifthlab.demo.dao.authentication.JCaptchaServiceProxyImpl"> class="com.octo.captcha.service.image.DefaultManageableImageCaptchaService" />
先解释一下上面applicationContext.xml中红色的部分是jCaptcha的一个filter,必须放在第二个位置,后面的是 acegi的filter,也就是说,要先验证图片验证码是否正确,再让acegi来验证用户名和密码。蓝色的部分是需要写的几个代码,其中有一个修复 bug
的地方,如果不添加那个bean,在运行的时候就会报Cast转型的错误。下面列一下蓝色部分的代码:
FixedCaptchaSecurityContextImpl:
import org.acegisecurity.captcha.CaptchaSecurityContextImpl;
public class FixedCaptchaSecurityContextImpl extends CaptchaSecurityContextImpl { public int hashCode() {
if (getAuthentication() == null) {
return (int) System.currentTimeMillis();
} else {
return this.getAuthentication().hashCode();
}
}
}
Hibernate3Authentication:
import java.util.ArrayList;
import java.util.List;
import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import https://www.wendangku.net/doc/691517423.html,erDetails;
import https://www.wendangku.net/doc/691517423.html,erDetailsService;
import https://www.wendangku.net/doc/691517423.html,ernameNotFoundException;
import org.hibernate.Criteria;
import org.hibernate.criterion.Expression;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.fifthlab.demo.pojo.Roletest;
import https://www.wendangku.net/doc/691517423.html,ertest;
public class Hibernate3Authentication extends HibernateDaoSupport implements UserDetailsService {
public UserDetails loadUserByUsername(final String username)
{
Criteria criteria =
getHibernateTemplate().getSessionFactory().getCurrentSession().createCriteria(U sertest.class);
criteria.add(Expression.eq(Usertest.PROP_USERNAME, username));
List users = criteria.list();
if (users.size() == 0)
{
throw new UsernameNotFoundException("User not found");
}
Usertest user = (Usertest) users.get(0);
// get User roles of this user
List
userRoles.add(user.getRoletest());
GrantedAuthority[] dbAuths = new GrantedAuthority[userRoles.size()];
if (userRoles.size() == 0)
{
throw new UsernameNotFoundException("User has no GrantedAuthority");
}
// grant authority to user
String roleName = null;
Roletest role = null;
for (int i = 0; i < userRoles.size(); i++)
{
role = (Roletest) userRoles.get(i);
if (null != role)
{
roleName = role.getRolename();
dbAuths[i] = new GrantedAuthorityImpl(roleName);
}
}
user.setAuthorities(dbAuths);
return user;
}
}
JCaptchaServiceProxyImpl:
import org.acegisecurity.captcha.CaptchaServiceProxy;
import com.octo.captcha.service.image.ImageCaptchaService;
import com.octo.captcha.service.CaptchaServiceException;
public class JCaptchaServiceProxyImpl implements CaptchaServiceProxy {
private ImageCaptchaService jcaptchaService;
public boolean validateReponseForId(String id, Object response) {
try {
boolean result = jcaptchaService.validateResponseForID(id, response);
//System.out.println("id:"+id+"\nresponse:"+response+"\nresult:"+result); return result;
} catch (CaptchaServiceException cse) {
//fixes known bug in JCaptcha
cse.printStackTrace();
return false;
}
}
public void setJcaptchaService(ImageCaptchaService jcaptchaService) {
this.jcaptchaService = jcaptchaService;
}
}
接下来是简单的登录页面login.jsp和登录成功后的跳转页面success.jsp:
login.jsp:
<%@ page language="Java" contentType="text/html;charset=utf-8"%>
<%@ taglib uri="https://www.wendangku.net/doc/691517423.html,/jsf/html" prefix="h"%>
<%@ taglib uri="https://www.wendangku.net/doc/691517423.html,/jsf/core" prefix="f"%>
<%@ taglib uri="https://https://www.wendangku.net/doc/691517423.html,/ajax" prefix="a4j"%>
createContent="#{captchaImageBean.paint}" id="showPic" mimeType="image/jpeg" /> action="#{loginBean.authenticate}" /> 其中红色部分是应用了Ajax4jsf这个框架的两个标签, success.jsp的页面很简单,就是显示一个“Welcome here”的字样,因此不列出来了。 然后我们看一下页面中用到的backing bean的代码: LoginBean: import javax.faces.context.FacesContext; import javax.servlet.http.HttpSession; import org.acegisecurity.Authentication; import org.acegisecurity.AuthenticationException; import org.acegisecurity.AuthenticationManager; import org.acegisecurity.context.SecurityContext; import org.acegisecurity.context.SecurityContextHolder; import https://www.wendangku.net/doc/691517423.html,ernamePasswordAuthenticationToken; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import com.fifthlab.demo.dao.authentication.JCaptchaServiceProxyImpl; public class LoginBean extends HibernateDaoSupport { private static final long serialVersionUID = 1L; private String userName; private String password; private String verifyingCode; private AuthenticationManager authenticationManager; private JCaptchaServiceProxyImpl captchaService; /** * @return 返回用户输入的验证码 */ public String getVerifyingCode() { return this.verifyingCode; } /** * @param verifyingCode 设置图片验证码 */ public void setVerifyingCode(String verifyingCode) { this.verifyingCode = verifyingCode; } /** * @return 返回captchaService来验证图片验证码 */ public JCaptchaServiceProxyImpl getCaptchaService() { return this.captchaService; } /** * @param captchaService 设置图片验证码服务 */ public void setCaptchaService(JCaptchaServiceProxyImpl captchaService) { this.captchaService = captchaService; } /** * @return 返回密码 */ public String getPassword() { return password; } /** * @param password 设置密码 * */ public void setPassword(String password) { this.password = password; } /** * @return 返回用户名 */ public String getUserName() { return userName; } /** * @param userName 设置用户名 * */ public void setUserName(String userName) { https://www.wendangku.net/doc/691517423.html,erName = userName; } /** * @return 返回验证管理器 */ public AuthenticationManager getAuthenticationManager() { return authenticationManager; } /** * @param authenticationManager 设置验证管理器 * */ public void setAuthenticationManager(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } public String authenticate() { try { String sessionid = ((HttpSession)FacesContext.getCurrentInstance() .getExternalContext() .getSession(false)).getId(); boolean captchaResult = getCaptchaService().validateReponseForId(sessionid, getVerifyingCode());//验证图片验证码 UsernamePasswordAuthenticationToken authReq = new UsernamePasswordAuthenticationToken( getUserName(), getPassword()); //验证用户名和密码 Authentication auth = getAuthenticationManager().authenticate(authReq); String result = "failure"; if(auth.isAuthenticated() == true && captchaResult) {//如果图片验证码和帐户全部验证通过 result = "succeed"; //把验证信息保存的上下文中 SecurityContext secCtx = SecurityContextHolder.getContext(); secCtx.setAuthentication(auth); } return result; } catch(AuthenticationException e) { e.printStackTrace(); return "failure"; } } } CaptchaImageBean: import java.awt.image.BufferedImage; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.faces.context.FacesContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import com.octo.captcha.service.image.ImageCaptchaService; import com.sun.image.codec.jpeg.JPEGCodec; import com.sun.image.codec.jpeg.JPEGImageEncoder; public class CaptchaImageBean { static final long serialVersionUID = 1L; private ImageCaptchaService jcaptchaService; public void setJcaptchaService(ImageCaptchaService jcaptchaService) { this.jcaptchaService = jcaptchaService; } public void paint(OutputStream out, Object data) throws IOException { byte[] captchaChallengeAsJpeg = null; ByteArrayOutputStream jpegOutputStream = new ByteArrayOutputStream(); HttpServletRequest request = (HttpServletRequest)FacesContext.getCurrentInstance() .getExternalContext().getRequest(); String captchaId = ((HttpSession)FacesContext.getCurrentInstance() .getExternalContext() .getSession(false)).getId(); BufferedImage challenge = jcaptchaService.getImageChallengeForID( captchaId, request.getLocale()); JPEGImageEncoder jpegEncoder = JPEGCodec.createJPEGEncoder(jpegOutputStream); jpegEncoder.encode(challenge); captchaChallengeAsJpeg = jpegOutputStream.toByteArray(); jpegOutputStream.close(); out.write(captchaChallengeAsJpeg); out.flush(); out.close(); } } 最后当然是配置faces-config.xml了,里面配置了和Spring的整合、导航、managed bean 等等: org.springframework.web.jsf.DelegatingVariableResolver 大功告成!让我们来看一下演示吧: 填好帐户和验证码后,点击Submit,如果失败则还是该页不变,如果成功则跳转到: 不知道大家有没有实验成功?有任何问题可以给我留言!P.S. 需要的各种lib列表如下图: