通过xml配置方式实现数据库查询认证,的确简单但是不够灵活。但是如果登录验证逻辑稍微复杂些,可能通过这种配置方式就不能满足需求了,比如:当用户登录时,需要判断该用户是否绑定了邮箱,如果未绑定,拒绝登录并给出提示信息。
遇到类似的情况,就需要使用自定义登录来完成,并且给出的提示信息也需要是自定义的。
自定义登录验证(默认实现QueryDatabaseAuthenticationHandler)
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类:
修改authenticateUsernamePasswordInternal方法中的代码为自己的认证逻辑即可。
操作步骤:
1,Eclipse中引入cas-server-webapp项目,并在lib下添加两个jar包
2,自定义一个类,这个类的内容可以是复制QueryDatabaseAuthenticationHandler
类中的核心方法:
@Override 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 { /** * 以下的代码为CAS的数据库认证默认实现,如果想编写自己的实现方式,可以删除一下代码实现自己的登录认证 */ //sql为配置文件中配置的sql语句 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; } }
3,根据业务需求编写自己的自定义登录方法,修改如下代码即可
final String dbPassword = getJdbcTemplate().queryForObject(this.sql, String.class, username); return dbPassword.equals(encryptedPassword);
当然也可以访问进行
http://localhost:8080/cas-server-webapp/login 调试。
4,配置使自定义登录认证生效
<!-- 注释掉默认的配置,使用自定义类配置 --> <!-- <bean class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"> <property name="dataSource" ref="dataSource" ></property> <property name="sql" value="select password from t_user where login_name=?" ></property> <property name="passwordEncoder" ref="MD5PasswordEncoder" ></property> </bean> --> <bean class="com.tgb.handler.CustomQueryDBHandler"> <property name="dataSource" ref="dataSource" ></property> <property name="sql" value="select password from t_user where login_name=?" ></property> <!-- <property name="passwordEncoder" ref="MD5PasswordEncoder" ></property> --> </bean>
完成以上步骤,自定义登录即可实现!
自定义错误提示消息(默认实现IncorrectResultSizeDataAccessException继承自RuntimeException)
CAS AuthenticationException结构如下图,CAS已经内置了一些异常,比如用户名密码错误、未知的用户名错误等。
当用户名输入正确,而密码错误时提示“密码错误”
只需要在自定义的AuthenticationHandler类的验证方法中,验证失败的地方抛出异常即可。
密码错误的异常类:
请注意代码中的CODE私有属性,该属性定义了一个本地化资源文件中的键,通过该键获取本地化资源中对应语言的文字,这里只实现中文错误消息提示,修改WEB-INF/classes/messages_zh_CN.properties文件,添加CODE定义的键值对,如下示例:
error.authentication.credentials.bad.usernameorpassword.password=\u5bc6\u7801\u9519\u8bef
后面的文字是使用jdk自带的native2ascii编码工具:native2ascii转换成utf-8格式。
接下来只需要在自定义的AuthenticationHandler类的验证方法中,验证失败的地方抛出异常即可。
自定义AuthenticationHandler示例代码如下:
@Override 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 = null; if (dbPassword == null || dbPassword == "") { throw new BadPasswordAuthenticationException(); } return dbPassword.equals(encryptedPassword); } catch (final IncorrectResultSizeDataAccessException e) { // this means the username was not found. return false; }
配置使自定义错误提示生效 同上(配置自定义登录认证)!
效果:
总结:
通过以上的学习,我们知道CAS为我们提供了很多便利的扩展功能,它的这种方式也是我们在设计软件时必须要考虑和不断要加强的内容。