# 系统登录组件
# 登录认证器
登录认证器是一种认证单元,一个认证策略就需要实现一套认证器。典型应用场景如下:AD/LDAP统一认证、双因素认证、CA认证实现、帐号密码认证。
# 默认认证器
平台提供了多元化的认证器,只要任意一个认证器认证通过,即可当作登录成功。系统缺省提供几个验证器:
验证器 | 说明 |
---|---|
com.seeyon.ctp.portal.sso.login.SSOTicketLoginAuthentication | 单点登录认证 |
com.seeyon.ctp.login.auth.QrCodeLoginAuthentication | 二维码登录 |
com.seeyon.v3x.plugin.ca.CALoginAuthentication | 使用CA认证 |
com.seeyon.ctp.login.IdentificationDogLoginAuthentication | 使用身份验证狗认证 |
com.seeyon.apps.ldap.login.LDAPLoginAuthentication | 使用LDAP认证 |
com.seeyon.ctp.login.auth.CASLoginAuthentication | CIP集成平台CAS认证 |
com.seeyon.ctp.login.auth.SMSLoginAuthentication | 短信验证码认证 |
com.seeyon.ctp.login.auth.DefaultLoginAuthentication | 使用协同用户系统认证 |
# 实现原理
每一套认证器均是集成AbstractLoginAuthentication,重写authenticate方法,并按如下注释说明来控制成功还是失败:
/**
* 认证,策略是:
*
* <ol>
* <li>系统框架把登录界面发过来的<code>HttpServletRequest</code>完整的传递给认证实现类</li>
* <li>认证实现类完成自己的逻辑</li>
* <li>返回值约定
* <ol type="i">
* <li>当验证通过,需要给框架返回[用户名, 密码],框架将直接跳转到首页</li>
* <li>当不验证通过,但要终止本次登录请求,直接throw new LoginAuthenticationException(),用户将跳转到登录页</li>
* <li>当不验证通过,返回null,框架将调用下一个认证类认证</li>
* </ol>
* </li>
* </ol>
*
* @return 认证通过: 返回[用户名, 密码]; 验证不通过返回null,框架将调用下一个认证类认证
* @throws LoginAuthenticationException 用户将跳转到登录页
*/
String[] authenticate(HttpServletRequest request, HttpServletResponse response)
throws LoginAuthenticationException;
# 实现示例
要实现自己的登录认证,必须:
1、继承com.seeyon.ctp.login.AbstractLoginAuthentication,实现自己的登录验证类
package com.seeyon.apps.login;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.seeyon.ctp.common.constants.Constants;
import com.seeyon.ctp.common.constants.LoginConstants;
import com.seeyon.ctp.login.AbstractLoginAuthentication;
import com.seeyon.ctp.login.LoginAuthenticationException;
public class CustomLoginAuthentication extends AbstractLoginAuthentication {
@Override
public String[] authenticate(HttpServletRequest request,
HttpServletResponse response) throws LoginAuthenticationException {
String username = request.getParameter(LoginConstants.USERNAME);// 用户名
String password = request.getParameter(LoginConstants.PASSWORD);// 密码
if (username == null || password == null) {
return null;
}
//登录方式,判断是否移动应用登陆
String userAgentFrom = request.getParameter(Constants.LOGIN_USERAGENT_FROM);
boolean fromMobile = Constants.login_useragent_from.mobile.name().equals(userAgentFrom) || LoginUtil.isFromM1(userAgentFrom);
if (check(username, password)) {
return new String[] { username, password };
}
return null;
}
private boolean check(String username, String password) {
// 登录认证逻辑,用户名和密码正确时返回true即可
return false;
}
}
2、将自定义认证器注册到Spring bean中:
<bean class="com.seeyon.apps.login.CustomLoginAuthentication"/>
# 登录认证拦截器
登录认证拦截器用于在认证前、认证后进行拦截、记录登录行为以及做前置、后置动作。
典型应用场景如下:登录认证前做IP访问控制、登录成功后记录到审计日志、计算登录前、后执行耗时。
登录认证拦截器使用Spring的Interceptor拦截器实现。
# 默认登录拦截器
系统缺省通过几个拦截器实现一些功能控制:
验证器 | 说明 |
---|---|
com.seeyon.ctp.login.interceptor.VerifyCodeLoginInterceptor | 验证码拦截器,在preHandle对用户输入的验证码进行校验 |
com.seeyon.ctp.login.interceptor.LockLoginInterceptor | 锁定用户拦截器 |
com.seeyon.ctp.login.interceptor.IpcontrolLoginInterceptor | IP控制拦截器 |
com.seeyon.ctp.login.interceptor.MutilBrowserLoginInterceptor | 限制管理员只能使用PC,通过IE登录 |
# 实现原理
每一个拦截器均继承自AbstractLoginInterceptor,并根据使用场景重写“登录前preHandle”、“登录后afterComplete”、“登录失败afterFailure”的方法:
/**
* 登录之前的操作
*
* 此时AppContext.getCurrentUser()是null的
*
* @param request
* @param response
* @return Error标示本次登录终止,返回到登录页;OK正常往下进行
*/
public LoginResult preHandle(HttpServletRequest request, HttpServletResponse response);
/**
* 在登录验证成功后的操作
* 此时AppContext.getCurrentUser()有值
* @param request
* @param response
* @return Error标示本次登录终止,返回到登录页;OK正常往下进行
*/
public LoginResult afterComplete(HttpServletRequest request, HttpServletResponse response);
/**
* 在登录验证失败后的操作
*
* @param request
* @param response
* @return Error不管怎么样都会跳转到登录页面,最好别返回LoginResult.OK他也没有用
*/
public LoginResult afterFailure(HttpServletRequest request, HttpServletResponse response);
# 实现示例
可以通过以下步骤实现登录拦截:
1、继承com.seeyon.ctp.login.AbstractLoginInterceptor,实现自己的登录拦截器类
package com.seeyon.apps.login;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.seeyon.ctp.common.constants.LoginConstants;
import com.seeyon.ctp.login.AbstractLoginInterceptor;
import com.seeyon.ctp.login.LoginAuthenticationException;
public class CustomMutilBrowserLoginInterceptor extends com.seeyon.ctp.login.AbstractLoginInterceptor {
public CustomMutilBrowserLoginInterceptor() {
}
//管理员只能通过IE、PC访问
public LoginResult afterComplete(HttpServletRequest request,HttpServletResponse response) {
User currentUser = CurrentUser.get();
if(currentUser.isAdmin()) {
Boolean A8Allow4Admin = (Boolean)(BrowserFlag.A8Allow4Admin.getFlag(request));
//管理员只能从IE上登录
if (Boolean.FALSE.equals(A8Allow4Admin)) {
return LoginResult.ERROR_IPCONTROLIPAD;
}
//管理员不能从M1上登录
if(currentUser.isFromM1() || Constants.login_useragent_from.mobile.name().equals(currentUser.getUserAgentFrom())){
return LoginResult.ERROR_ForbiddenAdminLogin;
}
}
return LoginResult.OK;
}
}
2、注册Spring bean,将实现类在自己的Spring插件配置下注册:
<bean class="com.seeyon.apps.login.CustomMutilBrowserLoginInterceptor"/>
# 执行顺序调整
在大多数场景下,同一套产品只会有一个主要的LoginAuthentication生效,而LoginInterceptor对顺序不敏感,原则上不需要排序。
但如果要对LoginAuthentication和LoginInterceptor进行排序,可以在插件目录定义一个login.xml,如(WEB-INF\cfgHome\plugin\myplugin\login.xml),并按如下配置示例进行属性配置:
<?xml version="1.0" encoding="UTF-8"?>
<login>
<!-- 将CustomLoginInterceptor放到com.seeyon.ctp.login.interceptor.LockLoginInterceptor之前执行。 -->
<bean class="com.seeyon.apps.login.CustomLoginInterceptor" before1="com.seeyon.ctp.login.interceptor.LockLoginInterceptor"/>
<!-- 将CustomLoginAuthentication放到com.seeyon.v3x.plugin.ca.CALoginAuthentication之后执行。 -->
<bean class="com.seeyon.apps.login.CustomLoginAuthentication" after2="com.seeyon.v3x.plugin.ca.CALoginAuthentication"/>
</login>
一旦定义了login.xml,对应的bean就无需在spring xml中注册。
