CAS自定义登录验证方法
一、CAS登录认证原理
CAS认证流程如下图:

CAS服务器的org.jasig.cas.authentication.AuthenticationManager负责基于提供的凭证信息进行用户认证。与Spring Security很相似,实际的认证委托给了一个或多个实现了org.jasig.cas.authentication.handler.AuthenticationHandler接口的处理类。
最后,一个org.jasig.cas.authentication.principal.CredentialsToPrincipalResolver用来将传递进来的安全实体信息转换成完整的org.jasig.cas.authentication.principal.Principal(类似于Spring Security中UserDetailsService实现所作的那样)。
二、自定义登录认证
CAS内置了一些AuthenticationHandler实现类,如下图所示,在cas-server-support-jdbc包中提供了基于jdbc的用户认证类。

如果需要实现自定义登录,只需要实现org.jasig.cas.authentication.handler.AuthenticationHandler接口即可,当然也可以利用已有的实现,比如创建一个继承自 org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler的类,实现方法可以参考org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler类:
package org.jasig.cas.adaptors.jdbc;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import javax.validation.constraints.NotNull;
public final class QueryDatabaseAuthenticationHandler extends
AbstractJdbcUsernamePasswordAuthenticationHandler {
@NotNull
private String sql;
protected final boolean authenticateUsernamePasswordInternal(final UsernamePasswordCredentials credentials) throws AuthenticationException {
final String username = getPrincipalNameTransformer().transform(credentials.getUsername());
final String password = credentials.getPassword();
final String encryptedPassword = this.getPasswordEncoder().encode(
password);
try {
final String dbPassword = getJdbcTemplate().queryForObject(
this.sql, String.class, username);
return dbPassword.equals(encryptedPassword);
} catch (final IncorrectResultSizeDataAccessException e) {
// this means the username was not found.
return false;
}
}
/**
* @param sql The sql to set.
*/
public void setSql(final String sql) {
this.sql = sql;
}
}
修改authenticateUsernamePasswordInternal方法中的代码为自己的认证逻辑即可。
注意:不同版本的handler实现上稍有差别,请参考对应版本的hanlder,本文以3.4为例。
三、自定义登录错误提示消息
CAS核心类CentralAuthenticationServiceImpl负责进行登录认证、创建TGT、ST、验证票据等逻辑,该类中注册了CAS认证管理器AuthenticationManager,对应bean的配置如下:
<bean id="centralAuthenticationService" class="org.jasig.cas.CentralAuthenticationServiceImpl"
p:ticketGrantingTicketExpirationPolicy-ref="grantingTicketExpirationPolicy"
p:serviceTicketExpirationPolicy-ref="serviceTicketExpirationPolicy"
p:authenticationManager-ref="authenticationManager"
p:ticketGrantingTicketUniqueTicketIdGenerator-ref="ticketGrantingTicketUniqueIdGenerator"
p:ticketRegistry-ref="ticketRegistry" p:servicesManager-ref="servicesManager"
p:persistentIdGenerator-ref="persistentIdGenerator"
p:uniqueTicketIdGeneratorsForService-ref="uniqueIdGeneratorsMap" />
CentralAuthenticationServiceImpl中的方法负责调用AuthenticationManager进行认证,并捕获AuthenticationException类型的异常,如创建ST的方法grantServiceTicket代码示例如下:
if (credentials != null) {
try {
final Authentication authentication = this.authenticationManager
.authenticate(credentials);
final Authentication originalAuthentication = ticketGrantingTicket.getAuthentication();
if (!(authentication.getPrincipal().equals(originalAuthentication.getPrincipal()) && authentication.getAttributes().equals(originalAuthentication.getAttributes()))) {
throw new TicketCreationException();
}
} catch (final AuthenticationException e) {
throw new TicketCreationException(e);
}
}
在CAS WEBFLOW流转的过程中,对应的action就会捕获这些TicketCreationException,并在表单中显示该异常信息。
如org.jasig.cas.web.flow.AuthenticationViaFormAction类中的表单验证方法代码如下:
public final String submit(final RequestContext context, final Credentials credentials, final MessageContext messageContext) throws Exception {
final String ticketGrantingTicketId = WebUtils.getTicketGrantingTicketId(context);
final Service service = WebUtils.getService(context);
if (StringUtils.hasText(context.getRequestParameters().get("renew")) && ticketGrantingTicketId != null && service != null) {
try {
final String serviceTicketId = this.centralAuthenticationService.grantServiceTicket(ticketGrantingTicketId, service, credentials);
WebUtils.putServiceTicketInRequestScope(context, serviceTicketId);
putWarnCookieIfRequestParameterPresent(context);
return "warn";
} catch (final TicketException e) {
if (e.getCause() != null && AuthenticationException.class.isAssignableFrom(e.getCause().getClass())) {
populateErrorsInstance(e, messageContext);
return "error";
}
this.centralAuthenticationService.destroyTicketGrantingTicket(ticketGrantingTicketId);
if (logger.isDebugEnabled()) {
logger.debug("Attempted to generate a ServiceTicket using renew=true with different credentials", e);
}
}
}
try {
WebUtils.putTicketGrantingTicketInRequestScope(context, this.centralAuthenticationService.createTicketGrantingTicket(credentials));
putWarnCookieIfRequestParameterPresent(context);
return "success";
} catch (final TicketException e) {
populateErrorsInstance(e, messageContext);
return "error";
}
}
因此在自定义的AuthenticationHandler类的验证方法中抛出继承自AuthenticationException的异常,登录页面(默认为WEB-INF/view/jsp/default/ui/casLoginView.jsp)中的Spring Security验证表单将会自动输出该异常对应的错误消息。
CAS AuthenticationException结构如下图,CAS已经内置了一些异常,比如用户名密码错误、未知的用户名错误等。

假设这样一个需求:用户注册时需要验证邮箱才能登录,如果未验证邮箱,则提示用户还未验证邮箱,拒绝登录。
为实现未验证邮箱后提示用户的需求,定义一个继承自AuthenticationException的类:UnRegisterEmailAuthenticationException,代码示例如下:
package test;
import org.jasig.cas.authentication.handler.BadUsernameOrPasswordAuthenticationException;
public class UnRegisterEmailAuthenticationException extends BadUsernameOrPasswordAuthenticationException {
/** Static instance of UnknownUsernameAuthenticationException. */
public static final UnRegisterEmailAuthenticationException ERROR = new UnRegisterEmailAuthenticationException();
/** Unique ID for serializing. */
private static final long serialVersionUID = 3977861752513837361L;
/** The code description of this exception. */
private static final String CODE = "error.authentication.credentials.bad.unregister.email";
/**
* Default constructor that does not allow the chaining of exceptions and
* uses the default code as the error code for this exception.
*/
public UnRegisterEmailAuthenticationException() {
super(CODE);
}
/**
* Constructor that allows for the chaining of exceptions. Defaults to the
* default code provided for this exception.
*
* @param throwable the chained exception.
*/
public UnRegisterEmailAuthenticationException(final Throwable throwable) {
super(CODE, throwable);
}
/**
* Constructor that allows for providing a custom error code for this class.
* Error codes are often used to resolve exceptions into messages. Providing
* a custom error code allows the use of a different message.
*
* @param code the custom code to use with this exception.
*/
public UnRegisterEmailAuthenticationException(final String code) {
super(code);
}
/**
* Constructor that allows for chaining of exceptions and a custom error
* code.
*
* @param code the custom error code to use in message resolving.
* @param throwable the chained exception.
*/
public UnRegisterEmailAuthenticationException(final String code,
final Throwable throwable) {
super(code, throwable);
}
}
请注意代码中的CODE私有属性,该属性定义了一个本地化资源文件中的键,通过该键获取本地化资源中对应语言的文字,这里只实现中文错误消息提示,修改WEB-INF/classes/messages_zh_CN.properties文件,添加CODE定义的键值对,如下示例:
error.authentication.credentials.bad.unregister.email=\u4f60\u8fd8\u672a\u9a8c\u8bc1\u90ae\u7bb1\uff0c\u8bf7\u5148\u9a8c\u8bc1\u90ae\u7bb1\u540e\u518d\u767b\u5f55
后面的文字是使用native2ascii工具编码转换的中文错误提示。
接下来只需要在自定义的AuthenticationHandler类的验证方法中,验证失败的地方抛出异常即可。
自定义AuthenticationHandler示例代码如下:
package cn.test.web;
import javax.validation.constraints.NotNull;
import org.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;
import org.jasig.cas.authentication.handler.AuthenticationException;
import org.jasig.cas.authentication.principal.UsernamePasswordCredentials;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
public class CustomQueryDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {
@NotNull
private String sql;
@Override
protected boolean authenticateUsernamePasswordInternal(UsernamePasswordCredentials credentials) throws AuthenticationException {
final String username = getPrincipalNameTransformer().transform(credentials.getUsername());
final String password = credentials.getPassword();
final String encryptedPassword = this.getPasswordEncoder().encode(password);
try {
// 查看邮箱是否已经验证。
Boolean isEmailValid= EmailValidation.Valid();
if(!isEmailValid){
throw new UnRegisterEmailAuthenticationException();
}
//其它验证
……
} catch (final IncorrectResultSizeDataAccessException e) {
// this means the username was not found.
return false;
}
}
public void setSql(final String sql) {
this.sql = sql;
}
}
三、配置使自定义登录认证生效
最后需要修改AuthenticationManager bean的配置(一般为修改WEB-INF/spring-configuration/applicationContext.xml文件),加入自定义的AuthenticationHandler,配置示例如下:
<bean id="authenticationManager" class="org.jasig.cas.authentication.AuthenticationManagerImpl">
<property name="credentialsToPrincipalResolvers">
<list>
<bean class="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver">
<property name="attributeRepository" ref="attributeRepository" />
</bean>
<bean class="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver" />
</list>
</property> <property name="authenticationHandlers">
<list>
<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
p:httpClient-ref="httpClient" p:requireSecure="false" />
<bean class="cn.test.web.CustomQueryDatabaseAuthenticationHandler">
<property name="sql" value="select password from t_user where user_name=?" />
<property name="dataSource" ref="dataSource" />
<property name="passwordEncoder" ref="passwordEncoder"></property>
</bean>
</list>
</property>
</bean>
CAS自定义登录验证方法的更多相关文章
- jQuery Validate 表单验证插件----自定义一个验证方法
一.下载依赖包 网盘下载:https://yunpan.cn/cryvgGGAQ3DSW 访问密码 f224 二.引入依赖包 <script src="../../scripts/j ...
- Spring Security 入门(3-11)Spring Security 的使用-自定义登录验证和回调地址
配置文件 security-ns.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmln ...
- jQuery Validate自定义各种验证方法(转)
一.封装自定义验证方法-validate-methods.js /***************************************************************** j ...
- SpringSecurity系列之自定义登录验证成功与失败的结果处理
一.需要自定义登录结果的场景 在我之前的文章中,做过登录验证流程的源码解析.其中比较重要的就是 当我们登录成功的时候,是由AuthenticationSuccessHandler进行登录结果处理,默认 ...
- jQuery Validation Engine 表单验证,自定义规则验证方法
jQuery Validation Engine 表单验证说明文档http://code.ciaoca.com/jquery/validation-engine/ js加到jquery.validat ...
- 自定义shiro实现权限验证方法isAccessAllowed
由于Shiro filterChainDefinitions中 roles默认是and, admin= user,roles[system,general] 比如:roles[system,gener ...
- Django自定义User模型和登录验证
用户表已存在(与其他App共用),不能再使用Django内置的User模型和默认的登录认证.但是还想使用Django的认证框架(真的很方便啊). 两个步骤: 1)自定义Use模型,为了区分系统的Use ...
- jquery validate 自定义验证方法
query validate有很多验证规则,但是更多的时候,需要根据特定的情况进行自定义验证规则. 这里就来聊一聊jquery validate的自定义验证. jquery validate有一个方法 ...
- jquery validate 自定义验证方法 日期验证
jquery validate有很多验证规则,但是更多的时候,需要根据特定的情况进行自定义验证规则. 这里就来聊一聊jquery validate的自定义验证. jquery validate有一个方 ...
随机推荐
- [VMware]设置VM虚拟机随系统自动启动
设置步骤: 1.找到VM的安装路径,右键vmware发送到桌面快捷方式 2.右键桌面快捷方式的属性,看到目标的属性框 3.找到需要自启动的虚拟机路径,如: D:\QC_VM\Clone of Wind ...
- find / -name *.py | xargs grep "domain" | wc -l
http://world77.blog.51cto.com/414605/209125 http://blog.csdn.net/windone0109/article/details/2817792 ...
- Hibernate之Criteria的完整用法
Criteria的完整用法 QBE (Query By Example) Criteria cri = session.createCriteria(Student.class); cri.add(E ...
- windows 搭建 solr 5.3.2
1. Tamcat 的安装,此不介绍 路径:F:\SolrTest\apache-tomcat-8.0.18 2. 解压 solr 5.3.2 路径:F:\Tool\solr-5.3.2 3. 复制s ...
- jquery实践案例--验证电子邮箱
<input type="email" name="email" id="email" value="" onpa ...
- [转]PhoneGap使用PushPlugin插件实现消息推送
本文转自:http://my.oschina.net/u/1270482/blog/217661 http://devgirl.org/2013/07/17/tutorial-implement-pu ...
- 浅谈Java中的深拷贝和浅拷贝(转载)
浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: ...
- 《TCP/IP详解 卷一》读书笔记-----UDP&IP 分片
1.进程每产生一个UDP数据报就由一个IP数据报进行发送,而在TCP中,一个IP数据报并不与每个TCP报文段一一对应 2.UDP的端口号和TCP的端口号是相互独立的,对那些众所周知的端口号TCP和UD ...
- 广度优先搜索 codevs 2806 红与黑
codevs 2806 红与黑 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 白银 Silver 题目描述 Description 有一个矩形房间,覆盖正方形瓷砖.每块瓷砖 ...
- 如何实现ZBrush中的Alt和Shift键的快速运用
ZBrush是一个数字雕刻和绘画软件,它以强大的功能和直观的工作流程彻底改变了整个三维雕刻行业.在一个简洁的界面中,ZBrush®为当代数字艺术家提供了世界上最先进的工具.利用快捷键能使操作更快捷高效 ...