cas 自定义登陆异常提示

本文介绍了如何在CAS中自定义登录错误提示,特别是针对未验证邮箱的场景。通过创建继承自AuthenticationException的UnRegisterEmailAuthenticationException,并配置AuthenticationManager,实现当用户未验证邮箱时显示特定的错误消息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

自定义登录错误提示消息

CAS核心类CentralAuthenticationServiceImpl负责进行登录认证、创建TGTST、验证票据等逻辑,该类中注册了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>
复制代码
 
 
参考原文地址:
http://www.cnblogs.com/gao241/p/3367805.html
### CAS访问出现`null`错误的原因分析 在使用CAS(Central Authentication Service)进行单点登录时,如果遇到返回`null`的情况,通常可能由以下几个方面引起: #### 1. **Ticket验证失败** 当客户端向CAS服务端发送ticket进行验证时,可能会因为某些原因导致验证失败,从而返回`null`。这种情况可能是由于以下原因之一引起的: - Ticket过期:CAS中的Service Ticket具有一定的有效期,在有效期内未完成认证则会被认为失效[^2]。 - 配置不一致:如果CAS Server和Client之间的配置参数存在差异(如service URL定义不同),可能导致无法正确解析或匹配ticket。 #### 2. **Session管理问题** 如果用户的会话信息丢失或者未能被正确存储,则可能出现获取不到用户身份标识的现象即得到的结果为`null`。具体来说, - 浏览器禁用了Cookie功能会影响session跟踪机制; - 多台服务器集群环境下缺少统一的session共享方案也可能造成此类现象发生[^3]。 #### 解决方案建议 针对上述提到的各种可能性提供如下几种解决办法供参考: #### 方案一:调整Timeout设置延长票据生命周期 可以通过修改deployerConfigContext.xml文件来增加ST(TicketGrantingTicket)以及TGT(ServiceTicket)的有效时间长度,防止因超时而导致的数据为空情况. ```xml <bean id="ticketGrantingTicketExpirationPolicy" class="org.jasig.cas.ticket.support.MultiTimeUseOrTimeoutExpirationPolicy"> <!-- 设置最大存活时间为8小时 --> <constructor-arg index="0" value="28800"/> </bean> ``` 此段代码片段展示了如何自定义设定Ticket Granting Tickets 的最长生存期限为例说明通过编程方式更改默认策略的方法之一[^1]. #### 方案二:确保前后端URL一致性 仔细核对cas client 中注册的服务地址与实际请求过来的一致性非常重要。任何细微差别都可能导致最终校验环节出现问题进而引发异常响应包含NULL值的情形出现。 另外还需要注意的是对于支持HTTPS协议下的站点而言除了基本路径之外还应该考虑SSL证书等因素的影响[^4]. #### 方案三:优化Session处理逻辑 为了应对分布式部署场景下可能存在的session同步难题可以从技术架构层面着手改进比如引入Redis作为集中式的缓存组件用来保存各个节点间的共同状态数据;或者是采用反向代理模式将所有外部流量引导至单一入口再分发给内部具体的实例执行业务操作以此规避多机房间通信带来的额外复杂度风险等问题的发生几率大大降低.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值