单点登录 - 自定义CAS服务器的登录功能

CAS的默认配置是使用以下的身份认证处理器进行验证

    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
        <property name="users">
            <map>
                <entry key="hugo" value="123456"/>
            </map>
        </property>
    </bean>

身份认证处理器AcceptUsersAuthenticationHandler通过获取预先设定的用户名密码,与从request中获取的用户名密码比对,判定认证是否通过。

但是,这种固定死账号密码的身份认证方式在项目中是不可能使用的,值得庆幸的是,CAS抽象了这个功能,这使得能满足我们对身份认证的各种需求。

在项目中,我们一般通过连接数据库获取用户的用户名和密码,然后认证其身份。接下来我们自定义身份认证的功能实现该需求。

1、添加依赖

除了需要添加MYSQL数据库的驱动、数据库连接池外,我们还需要添加cas的jdbc支持包 cas-server-support-jdbc,它给我们提供了一个抽象类AbstractJdbcUsernamePasswordAuthenticationHandler,它封装了JDBCTemplate和数据库连接池 

<dependency>
    <groupId>org.jasig.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>${project.version}</version>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.41</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.29</version>
</dependency>

 

2、建表

# 用户表
create table pms_user
(
  `id`                  bigint not null auto_increment,
  `remark`               varchar(300) COMMENT '备注',
  `login_name`           varchar(50) not null COMMENT '登录名',
  `login_pwd`            varchar(256) not null COMMENT '登录密码',
  `salt`                 varchar(50) not null COMMENT '密码盐值',
  primary key (id)
)ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='用户表';

-- -- 用户数据
--  admin 超级管理员
INSERT INTO pms_user (id, remark, login_name, login_pwd, salt)
VALUES (1, '超级管理员', 'admin', '7b0f43981f6b4469aec46395072692b7', 'KZTpzgMKyzQAvNR2SE1URw==');

--  guest  游客
INSERT INTO pms_user (id, remark, login_name, login_pwd, salt)
VALUES (2, '游客', 'guest', '98b67cab3f147441053f98c4ba62c965', 'NH9k1VTIJRb3yuaNMqZSOg==');

 

3、添加JDBC属性文件

在WEB-INF下添加jdbc.properties文件,并加入以下内容

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/cas-showcase?useUnicode=true&characterEncoding=utf-8
jdbc.username=root
jdbc.password=123456

#druid datasource
#参考 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE
druid.initialSize=10
druid.minIdle=10
druid.maxActive=50
druid.maxWait=60000
druid.timeBetweenEvictionRunsMillis=60000
druid.minEvictableIdleTimeMillis=300000
druid.validationQuery=SELECT 1
druid.testWhileIdle=true
druid.testOnBorrow=false
druid.testOnReturn=false
druid.poolPreparedStatements=true
druid.maxPoolPreparedStatementPerConnectionSize=20
druid.filters=wall,stat

# 身份鉴权器
jdbc.selectSQL=SELECT `login_pwd`,`salt` FROM pms_user WHERE login_name = ?
algorithmName=SHA-1
passwordFieldName=login_pwd
saltFieldName=salt

在propertyFileConfigurer.xml修改添加property-placeholder,引入jdbc属性文件。

<context:property-placeholder ignore-unresolvable="true" properties-ref="casProperties"/>
<context:property-placeholder ignore-unresolvable="true"
                              location="/WEB-INF/jdbc.properties"/>

 

4、自定义身份认证器,并添加配置

其中,passwordFieldName是密码在数据库里的名字,saltFieldName是盐值在数据库中的名字,sql是获取数据库信息的sql语句。这里校验密码的功能的实现因人而异。

public class QueryAndSaltDatabaseAuthenticationHandler extends AbstractJdbcUsernamePasswordAuthenticationHandler {

    private static final String DEFAULT_PASSWORD_FIELD = "password";
    private static final String DEFAULT_SALT_FIELD = "salt";

    @NotNull
    protected String sql;

    @NotNull
    protected String passwordFieldName = DEFAULT_PASSWORD_FIELD;

    @NotNull
    protected String saltFieldName = DEFAULT_SALT_FIELD;

    @NotNull
    protected String algorithmName;

    public QueryAndSaltDatabaseAuthenticationHandler(final DataSource dataSource,
                                                     final String sql,
                                                     final String algorithmName) {
        super();
        setDataSource(dataSource);
        this.algorithmName = algorithmName;
        this.sql = sql;
    }

    @Override
    protected final HandlerResult authenticateUsernamePasswordInternal(final UsernamePasswordCredential credential)
            throws GeneralSecurityException, PreventedException {
        final String username = credential.getUsername();
        final String encodedPsw = this.getPasswordEncoder().encode(credential.getPassword());

        try {
            //查询数据库的数据
            final Map<String, Object> values = getJdbcTemplate().queryForMap(this.sql, username);

            //效验密码
            final String digestedPassword = digestEncodedPassword(encodedPsw, values);
            if (!values.get(this.passwordFieldName).equals(digestedPassword)) {
                throw new FailedLoginException("Password does not match value on record.");
            }

        } catch (final IncorrectResultSizeDataAccessException e) {
            if (e.getActualSize() == 0) {
                throw new AccountNotFoundException(username + " not found with SQL query");
            } else {
                throw new FailedLoginException("Multiple records found for " + username);
            }
        } catch (final DataAccessException e) {
            throw new PreventedException("SQL exception while executing query for " + username, e);
        }

        return createHandlerResult(credential, new SimplePrincipal(username, null), null);
    }

    protected String digestEncodedPassword(final String encodedPassword, final Map<String, Object> values) {

        if (!values.containsKey(this.saltFieldName)) {
            throw new RuntimeException("Specified field name for salt does not exist in the results");
        }

        final String salt = values.get(this.saltFieldName).toString();
        return PasswordUtils.encryptPassword(encodedPassword, salt, this.algorithmName);
    }

    public final void setPasswordFieldName(final String passwordFieldName) {
        this.passwordFieldName = passwordFieldName;
    }

    public final void setSaltFieldName(final String saltFieldName) {
        this.saltFieldName = saltFieldName;
    }

}

接下来需要在deployerConfigContext.xml中配置认证器的bean。

首先,删除或者注释下面默认的认证处理器

    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.authentication.AcceptUsersAuthenticationHandler">
        <property name="users">
            <map>
                <entry key="hugo" value="123456"/>
            </map>
        </property>
    </bean>

添加自定义的身份认证处理器

   <!-- 自定义验证处理类 -->
    <bean id="primaryAuthenticationHandler"
          class="org.jasig.cas.adaptors.jdbc.QueryAndSaltDatabaseAuthenticationHandler">
        <constructor-arg name="dataSource" ref="dataSource" />
        <constructor-arg name="sql" value="${jdbc.selectSQL}" />
        <!-- 加密算法 -->
        <constructor-arg name="algorithmName" value="${algorithmName}"/>
        <!-- 数据库中密码字段 -->
        <property name="passwordFieldName" value="${passwordFieldName}" />
        <!-- 数据库中salt字段 -->
        <property name="saltFieldName" value="${saltFieldName}" />
    </bean>

添加数据源


    <!-- 数据源 -->
    <!--see https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="close">
        <!-- 基本属性 url、user、password -->
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <property name="initialSize" value="${druid.initialSize}"/>
        <property name="minIdle" value="${druid.minIdle}"/>
        <property name="maxActive" value="${druid.maxActive}"/>

        <!-- 配置获取连接等待超时的时间 -->
        <property name="maxWait" value="${druid.maxWait}"/>
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
        <property name="timeBetweenEvictionRunsMillis"
                  value="${druid.timeBetweenEvictionRunsMillis}"/>

        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
        <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}"/>

        <property name="validationQuery" value="${druid.validationQuery}"/>
        <property name="testWhileIdle" value="${druid.testWhileIdle}"/>
        <property name="testOnBorrow" value="${druid.testOnBorrow}"/>
        <property name="testOnReturn" value="${druid.testOnReturn}"/>

        <!-- 打开PSCache,并且指定每个连接上PSCache的大小  如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
        <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}"/>
        <property name="maxPoolPreparedStatementPerConnectionSize"
                  value="${druid.maxPoolPreparedStatementPerConnectionSize}"/>

        <property name="filters" value="wall"/>

    </bean>

 

接下来,启动服务器,一切正常。

转载于:https://my.oschina.net/thinwonton/blog/1421807

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值