基于CAS实现单点登录(SSO):自定义登录验证方法

通过配置方式实现数据库查询认证,的确简单但是不够灵活。但是如果登录验证逻辑稍微复杂些,可能通过配置方式就不能满足需求了,比如:当用户登录时,需要判断该用户是否绑定了邮箱,如果未绑定,拒绝登录并给出提示信息。

遇到类似的情况,就需要使用自定义登录来完成,并且给出的提示信息也是自定义的。

 

自定义登录认证

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类:

    

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. packageorg.jasig.cas.adaptors.jdbc;  
  2.       
  3.     importorg.jasig.cas.authentication.handler.AuthenticationException;  
  4.     importorg.jasig.cas.authentication.principal.UsernamePasswordCredentials;  
  5.     importorg.springframework.dao.IncorrectResultSizeDataAccessException;  
  6.       
  7.     importjavax.validation.constraints.NotNull;  
  8.       
  9.     public final class QueryDatabaseAuthenticationHandlerextends  
  10.        AbstractJdbcUsernamePasswordAuthenticationHandler {  
  11.       
  12.         @NotNull  
  13.         private String sql;  
  14.       
  15.         protected final booleanauthenticateUsernamePasswordInternal(final UsernamePasswordCredentialscredentials) throws AuthenticationException {  
  16.             final String username =getPrincipalNameTransformer().transform(credentials.getUsername());  
  17.             final String password =credentials.getPassword();  
  18.             final StringencryptedPassword = this.getPasswordEncoder().encode(  
  19.                 password);  
  20.               
  21.             try {  
  22.                 final String dbPassword =getJdbcTemplate().queryForObject(  
  23.                     this.sql,String.class, username);  
  24.                 returndbPassword.equals(encryptedPassword);  
  25.             } catch (finalIncorrectResultSizeDataAccessException e) {  
  26.                 // this means theusername was not found.  
  27.                 return false;  
  28.             }  
  29.         }  
  30.       
  31.         /** 
  32.          * @param sql The sql toset. 
  33.          */  
  34.         public void setSql(final Stringsql) {  
  35.             this.sql = sql;  
  36.         }  
  37.     }  

修改authenticateUsernamePasswordInternal方法中的代码为自己的认证逻辑即可。

注意:不同版本的handler实现上稍有差别,请参考对应版本的hanlder,本文以3.4为例。

 

自定义登录错误提示消息

在自定义的AuthenticationHandler类的验证方法中抛出继承自AuthenticationException的异常,

登录页面(默认为WEB-INF/view/jsp/default/ui/casLoginView.jsp)中的SpringSecurity验证表单将会自动输出

该异常对应的错误消息。

CAS AuthenticationException结构如下图,CAS已经内置了一些异常,比如用户名密码错误、未知的用户名错误等。


假设这样一个需求:用户注册时需要验证邮箱才能登录,如果未验证邮箱,则提示用户还未验证邮箱,拒绝登录。

为实现未验证邮箱后提示用户的需求,定义一个继承自AuthenticationException

类:UnRegisterEmailAuthenticationException,代码示例如下:


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package test;  
  2.      
  3.    importorg.jasig.cas.authentication.handler.BadUsernameOrPasswordAuthenticationException;  
  4.      
  5.    public classUnRegisterEmailAuthenticationException extendsBadUsernameOrPasswordAuthenticationException {  
  6.          
  7. ** Static instance ofUnknownUsernameAuthenticationException. */  
  8.        public static finalUnRegisterEmailAuthenticationException ERROR = newUnRegisterEmailAuthenticationException();  
  9.      
  10.        /** Unique ID for serializing.*/  
  11.        private static final longserialVersionUID = 3977861752513837361L;  
  12.      
  13.        /** The code description of thisexception. */  
  14.        private static final String CODE= "error.authentication.credentials.bad.unregister.email";  
  15.      
  16.        /** 
  17.         * Default constructor that doesnot allow the chaining of exceptions and 
  18.         * uses the default code as theerror code for this exception. 
  19.         */  
  20.        public UnRegisterEmailAuthenticationException(){  
  21.            super(CODE);  
  22.        }  
  23.      
  24.        /** 
  25.         * Constructor that allows forthe chaining of exceptions. Defaults to the 
  26.         * default code provided for thisexception. 
  27.         * 
  28.         * @param throwable the chainedexception. 
  29.         */  
  30.        publicUnRegisterEmailAuthenticationException(final Throwable throwable) {  
  31.            super(CODE,throwable);  
  32.        }  
  33.      
  34.        /** 
  35.         * Constructor that allows forproviding a custom error code for this class. 
  36.         * Error codes are often used toresolve exceptions into messages. Providing 
  37.         * a custom error code allows theuse of a different message. 
  38.         * 
  39.         * @param code the custom code touse with this exception. 
  40.         */  
  41.        publicUnRegisterEmailAuthenticationException(final String code) {  
  42.            super(code);  
  43.        }  
  44.      
  45.        /** 
  46.         * Constructor that allows forchaining of exceptions and a custom error 
  47.         * code. 
  48.         * 
  49.         * @param code the custom errorcode to use in message resolving. 
  50.         * @param throwable the chainedexception. 
  51.         */  
  52.        publicUnRegisterEmailAuthenticationException(final String code,  
  53.            final Throwable throwable){  
  54.            super(code,throwable);  
  55.        }  
  56.    }  


请注意代码中的CODE私有属性,该属性定义了一个本地化资源文件中的键,通过该键获取本地化资源中对

应语言的文字,这里只实现中文错误消息提示,修改WEB-INF/classes/messages_zh_CN.properties文件,添加

CODE定义的键值对,如下示例:
error.authentication.credentials.bad.unregister.email=\u4f60\u8fd8\u672a\u9a8c\u8bc1\u90ae\u7bb1\uff0c\u8bf

7\u5148\u9a8c\u8bc1\u90ae\u7bb1\u540e\u518d\u767b\u5f55

后面的文字是使用jdk自带的native2ascii编码工具:native2ascii、native2ascii-reverse,转换成utf-8格式。

接下来只需要在自定义的AuthenticationHandler类的验证方法中,验证失败的地方抛出异常即可。
自定义AuthenticationHandler示例代码如下:


[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. package cn.test.web;  
  2.     
  3.   importjavax.validation.constraints.NotNull;  
  4.     
  5.   importorg.jasig.cas.adaptors.jdbc.AbstractJdbcUsernamePasswordAuthenticationHandler;  
  6.   importorg.jasig.cas.authentication.handler.AuthenticationException;  
  7.   importorg.jasig.cas.authentication.principal.UsernamePasswordCredentials;  
  8.   importorg.springframework.dao.IncorrectResultSizeDataAccessException;  
  9.     
  10.   public classCustomQueryDatabaseAuthenticationHandler extendsAbstractJdbcUsernamePasswordAuthenticationHandler {  
  11.     
  12.       @NotNull  
  13.       private String sql;  
  14.     
  15.       @Override  
  16.       protected booleanauthenticateUsernamePasswordInternal(UsernamePasswordCredentials credentials)throws AuthenticationException {  
  17.           final String username = getPrincipalNameTransformer().transform(credentials.getUsername());  
  18.           final String password =credentials.getPassword();  
  19.           final String encryptedPassword= this.getPasswordEncoder().encode(password);  
  20.     
  21.           try {  
  22.     
  23.               // 查看邮箱是否已经验证。  
  24.               Boolean isEmailValid=EmailValidation.Valid();  
  25. (!isEmailValid){  
  26.                   throw newUnRegisterEmailAuthenticationException();  
  27.               }  
  28.     
  29.               //其它验证  
  30.               ……  
  31.     
  32.           } catch (finalIncorrectResultSizeDataAccessException e) {  
  33.               // this means theusername was not found.  
  34.               return false;  
  35.           }  
  36.       }  
  37.     
  38.       public void setSql(final Stringsql) {  
  39.           this.sql = sql;  
  40.       }  
  41.   }  

配置使自定义登录认证生效

最后需要修改AuthenticationManager bean的配置(一般为修改WEB-INF/spring-configuration/applicationContext.xml

件),加入自定义的AuthenticationHandler,配置示例如下:

    

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. <beanid="authenticationManager"class="org.jasig.cas.authentication.AuthenticationManagerImpl">  
  2.         <propertyname="credentialsToPrincipalResolvers">  
  3.             <list>  
  4.                 <beanclass="org.jasig.cas.authentication.principal.UsernamePasswordCredentialsToPrincipalResolver">  
  5.                     <propertyname="attributeRepository" ref="attributeRepository"/>  
  6.                 </bean>  
  7.                 <beanclass="org.jasig.cas.authentication.principal.HttpBasedServiceCredentialsToPrincipalResolver"/>  
  8.             </list>  
  9.         </property>  
  10.       
  11.         <propertyname="authenticationHandlers">  
  12.             <list>  
  13.                 <beanclass="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"  
  14.                    p:httpClient-ref="httpClient"p:requireSecure="false" />  
  15.    
  16.                <beanclass="cn.test.web.CustomQueryDatabaseAuthenticationHandler">  
  17.                     <propertyname="sql" value="select password from t_user whereuser_name=?" />  
  18.                     <propertyname="dataSource" ref="dataSource" />  
  19.                     <property name="passwordEncoder"ref="passwordEncoder"></property>  
  20.                 </bean>  
  21.             </list>  
  22.         </property>  
  23.     </bean>  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值