JSF常用的开发框架JSF(myfaces/richfaces/a4j)+spring+(Hibernate+ibatis),系统的登录认证是通过spring中的acegi来实现的,acegi的认证要求登录表单的action路径包括特定的关键字符,例如:j_acegi_login,当然这个可以随意定义,只要acegi和登录表单中的一样就行了。acegi过滤器一旦发现访问的url中包括j_acegi_login,并会触发认证filter工作,认证的结果,会进行跳转成功或失败页面,页面会刷新跳转的。
如果希望用ajax登录,也是通过acegi认证的方式处理,但认证结果不要求刷新跳转,而是通过ajax只刷新原登录页面上的部分信息就行,如何处理?下面讲解一下处理步骤:
第一步:增加AcegiAjaxFilter,用于截获登录表单的提交
public class AcegiAjaxFilter extends OncePerRequestFilter {
private static final Log logger = LogFactory.getLog(AcegiAjaxFilter.class);
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//检查提交的变量中是否有ajax变量,没有就直接交给acegi默认处理
if (request.getParameter("ajax") == null;) {
filterChain.doFilter(request, response);
return;
}
RedirectResponseWrapper redirectResponseWrapper = new RedirectResponseWrapper(response);
//acegi的filter chain处理认证,redirectResponseWrapper用于获取acegi认证处理后的跳转路径
filterChain.doFilter(request, redirectResponseWrapper);
if (redirectResponseWrapper.getRedirect() != null) {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
String redirectURL = redirectResponseWrapper.getRedirect();
//创建JSONObject对象,用于返回认证结果,便于ajax页面局部刷新
JSONObject json=new JSONObject();
try{
//在acegi认证失败跳转的url加上login_error=1
//此外判断是否成功
if (redirectURL.indexOf("login_error=1") == -1){
json.put("success",true);
//获取用户登录信息
LoginUser user= (User) request.getSession().getAttribute(Constant.ACEGI_SESSION_USER);
if (user!=null ){
json.put("name",user.getName());
json.put("lastIp",user.getLastLoginIP());
json.put("lastTime",user.getLastLoginTime());
json.put("currIp",user.getCurrentIP());
}
}else{//登录失败
json.put("success",false);
String errorMsg= ((AuthenticationException) request.getSession().getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage();
json.put("errorMsg",errorMsg);
}
}catch(JSONException e){
logger.error("AcegiAjaxFilter JSONException");
logger.error("message:"+e.getMessage());
}catch(Exception e){
logger.error("AcegiAjaxFilter Exception");
logger.error("message:"+e.getMessage());
}
//把json数据写入response返回到页面
response.getOutputStream().write(json.toString().getBytes("UTF-8"));
}
}
}
第二步:配置web.xml,确保acegiAjaxFilter的mapping在acegi filter的前面,mapping是有匹配顺序的
<filter>
<filter-name>acegiAjaxFilter</filter-name>
<filter-class>com.aiplay.portal.web.filter.AcegiAjaxFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>acegiAjaxFilter</filter-name>
<url-pattern>/j_acegi_security_check</url-pattern>
</filter-mapping>
....
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步:修改acegi的配置文件
authenticationProcessingFilter中的authenticationFailureUrl属性,在原来设定的url后面加上login_error=1,便于ajaxFilter捕获到;
authenticationProcessingFilter中的filterProcessesUrl属性,要确保和acegiAjaxFilter的<filter-mapping>中设定的url-pattern,以及登录表单的action中的关键字符一样。
第四步:登录页面上利用jQuery和ajax进行处理
记得页面上要加载jquery.form.js库。
//用户登录 ajax实现
function ajaxLogin() {
var options = {
target: '#loginMessage', // target element(s) to be updated with server response
beforeSubmit: showWaitting, // pre-submit callback
success: successHandler, // post-submit callback
dataType: 'json', // 'xml', 'script', or 'json' (expected server response type)
clearForm: true // clear all form fields after successful submit
// url: 'j_acegi_security_check' // override for form's 'action' attribute
// type: 'post', // 'get' or 'post', override for form's 'method' attribute
// resetForm: true // reset the form after successful submit
// timeout: 3000 // $.ajax options can be used here too, for example:
};
$('#loginForm').ajaxSubmit(options);
}
//提交之前执行,可以设定页面上出现等待符号
function showWaitting(formData, jqForm, options) {
....
return true;
}
function successHandler(responseText, statusText, xhr, $form) {
//responseText是json object
if(responseText.success){
//登录成功处理
...
$("#info1").html(responseText.name);
$("#info2").html(responseText.lastIp);
$("#info3").html(responseText.lastTime);
$("#info3").html(responseText.currIp);
}else{
//登录失败处理
....
}
}
第五步:登录表单中的配置
action中的url最后要包含和authenticationProcessingFilter中的filterProcessesUrl属性以及acegiAjaxFilter的<filter-mapping>中设定的url-pattern一样的字符,
这里假如设成:j_acegi_security_check
在表单中添加<input type="hidden" name="ajax"/>,上面第一步的AcegiAjaxFilter代码中有一段
if (request.getParameter("ajax") == null;) {
filterChain.doFilter(request, response);
return;
}
这是用于检查提交的表单是否要求acegiAjaxFilter来处理,毕竟有的系统中有多个登录页面,并不是所有的都要求ajax处理。
如果希望用ajax登录,也是通过acegi认证的方式处理,但认证结果不要求刷新跳转,而是通过ajax只刷新原登录页面上的部分信息就行,如何处理?下面讲解一下处理步骤:
第一步:增加AcegiAjaxFilter,用于截获登录表单的提交
public class AcegiAjaxFilter extends OncePerRequestFilter {
private static final Log logger = LogFactory.getLog(AcegiAjaxFilter.class);
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//检查提交的变量中是否有ajax变量,没有就直接交给acegi默认处理
if (request.getParameter("ajax") == null;) {
filterChain.doFilter(request, response);
return;
}
RedirectResponseWrapper redirectResponseWrapper = new RedirectResponseWrapper(response);
//acegi的filter chain处理认证,redirectResponseWrapper用于获取acegi认证处理后的跳转路径
filterChain.doFilter(request, redirectResponseWrapper);
if (redirectResponseWrapper.getRedirect() != null) {
request.setCharacterEncoding("UTF-8");
response.setContentType("text/plain;charset=utf-8");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
response.setHeader("Pragma", "no-cache");
String redirectURL = redirectResponseWrapper.getRedirect();
//创建JSONObject对象,用于返回认证结果,便于ajax页面局部刷新
JSONObject json=new JSONObject();
try{
//在acegi认证失败跳转的url加上login_error=1
//此外判断是否成功
if (redirectURL.indexOf("login_error=1") == -1){
json.put("success",true);
//获取用户登录信息
LoginUser user= (User) request.getSession().getAttribute(Constant.ACEGI_SESSION_USER);
if (user!=null ){
json.put("name",user.getName());
json.put("lastIp",user.getLastLoginIP());
json.put("lastTime",user.getLastLoginTime());
json.put("currIp",user.getCurrentIP());
}
}else{//登录失败
json.put("success",false);
String errorMsg= ((AuthenticationException) request.getSession().getAttribute(AbstractProcessingFilter.ACEGI_SECURITY_LAST_EXCEPTION_KEY)).getMessage();
json.put("errorMsg",errorMsg);
}
}catch(JSONException e){
logger.error("AcegiAjaxFilter JSONException");
logger.error("message:"+e.getMessage());
}catch(Exception e){
logger.error("AcegiAjaxFilter Exception");
logger.error("message:"+e.getMessage());
}
//把json数据写入response返回到页面
response.getOutputStream().write(json.toString().getBytes("UTF-8"));
}
}
}
第二步:配置web.xml,确保acegiAjaxFilter的mapping在acegi filter的前面,mapping是有匹配顺序的
<filter>
<filter-name>acegiAjaxFilter</filter-name>
<filter-class>com.aiplay.portal.web.filter.AcegiAjaxFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>acegiAjaxFilter</filter-name>
<url-pattern>/j_acegi_security_check</url-pattern>
</filter-mapping>
....
<filter-mapping>
<filter-name>Acegi Filter Chain Proxy</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
第三步:修改acegi的配置文件
authenticationProcessingFilter中的authenticationFailureUrl属性,在原来设定的url后面加上login_error=1,便于ajaxFilter捕获到;
authenticationProcessingFilter中的filterProcessesUrl属性,要确保和acegiAjaxFilter的<filter-mapping>中设定的url-pattern,以及登录表单的action中的关键字符一样。
第四步:登录页面上利用jQuery和ajax进行处理
记得页面上要加载jquery.form.js库。
//用户登录 ajax实现
function ajaxLogin() {
var options = {
target: '#loginMessage', // target element(s) to be updated with server response
beforeSubmit: showWaitting, // pre-submit callback
success: successHandler, // post-submit callback
dataType: 'json', // 'xml', 'script', or 'json' (expected server response type)
clearForm: true // clear all form fields after successful submit
// url: 'j_acegi_security_check' // override for form's 'action' attribute
// type: 'post', // 'get' or 'post', override for form's 'method' attribute
// resetForm: true // reset the form after successful submit
// timeout: 3000 // $.ajax options can be used here too, for example:
};
$('#loginForm').ajaxSubmit(options);
}
//提交之前执行,可以设定页面上出现等待符号
function showWaitting(formData, jqForm, options) {
....
return true;
}
function successHandler(responseText, statusText, xhr, $form) {
//responseText是json object
if(responseText.success){
//登录成功处理
...
$("#info1").html(responseText.name);
$("#info2").html(responseText.lastIp);
$("#info3").html(responseText.lastTime);
$("#info3").html(responseText.currIp);
}else{
//登录失败处理
....
}
}
第五步:登录表单中的配置
action中的url最后要包含和authenticationProcessingFilter中的filterProcessesUrl属性以及acegiAjaxFilter的<filter-mapping>中设定的url-pattern一样的字符,
这里假如设成:j_acegi_security_check
在表单中添加<input type="hidden" name="ajax"/>,上面第一步的AcegiAjaxFilter代码中有一段
if (request.getParameter("ajax") == null;) {
filterChain.doFilter(request, response);
return;
}
这是用于检查提交的表单是否要求acegiAjaxFilter来处理,毕竟有的系统中有多个登录页面,并不是所有的都要求ajax处理。