CAS5.3自定义密码(数据库)认证(二)

自定义登录验证器

整合spring+Mybatis连接数据库进行密码认证

1.首先配置pom.xml文件

        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-webapp${app.server}</artifactId>
            <version>${cas.version}</version>
            <type>war</type>
            <scope>runtime</scope>
        </dependency>
        <!-- 校验ticket相关代码(方便追踪代码) -->
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-util</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-util-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-services-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-authentication</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-authentication-api</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apereo.cas</groupId>
            <artifactId>cas-server-core-authentication-attributes</artifactId>
            <version>${cas.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.29</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.16.18</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-classic</artifactId>
                </exclusion>
            </exclusions>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>

2.配置application.properties文件(这里只是部分内容)

##
# CAS Server Context Configuration
#
server.context-path=/
server.port=443
#指定cas.server.namecas.server.name
cas.server.name=https://cas.xxx.mtn
cas.server.prefix=${cas.server.name}/
##
# CAS Authentication Credentials
#
#cas.authn.accept.users=casuser::Mellon
#数据库连接
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/cas5.3.14?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
jdbc.user=root
jdbc.password=123456

#mybatis数据源配置
spring.datasource.url=${jdbc.url}
spring.datasource.driver-class-name=${jdbc.driverClass}
spring.datasource.username=${jdbc.user}
spring.datasource.password=${jdbc.password}
mybatis.mapperLocations=classpath:mapper/*.xml

##默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
cas.authn.jdbc.query[0].passwordEncoder.type=NONE
cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
#登入成功后可以查看到的信息
cas.authn.jdbc.query[0].principalAttributeList=userAccount:userAccount,userName:userName,email:email

3.配置spring

(1)配置spring,为了让spring加载 gds.application.cas.model包下 标注了@Service @Component @Controller 等注解的Bean

/**
 * 这个配置是空值,是为了让spring 加载 这个包下 标注了  @Service @Component @Controller 等注解的Bean
 * 并需要在resource/META-INF/spring.factories 中配置
 *
 * @author dongqifei
 */
@Configuration
@ComponentScan("gds.application.cas.model")
public class SpringConfig {
}

spring.factories 中配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  gds.application.cas.config.spring.SpringConfig

(2)开发功能接口,就和平常开发spring+springMVC项目一样写就行,只要确保代码在上面扫描的包下面即可。下图是我自己建的包,仅供参考
在这里插入图片描述
(3)这里可以写一个简单的Controller测试接口是否可以正常连接数据库

4.自定义登录验证器

(1)编写登录验证器

package gds.application.cas.config.principal;

import com.google.common.collect.Multimap;
import gds.application.cas.authentication.adaptors.CustomDatabaseAuthenticationHandler;
import gds.application.cas.model.usercenter.service.UserService;
import org.apereo.cas.authentication.*;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.authentication.support.password.PasswordEncoderUtils;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.configuration.model.support.jdbc.QueryJdbcAuthenticationProperties;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.util.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
import java.util.Map;

@Configuration("customQueryDatabaseAuthenticationConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomDatabaseAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {

    @Autowired
    private CasConfigurationProperties casProperties;

    @Autowired
    @Qualifier("servicesManager")
    private ServicesManager servicesManager;

    @Autowired
    private UserService userService;//注入自定义的接口

    /**
     * 将自定义验证器注册为Bean
     * @return
     */
    @Bean
    public AuthenticationHandler customAuthenticationConfiguration() {
        // 获取配置文件中配置的jdbc配置
        final List<QueryJdbcAuthenticationProperties> jdbc = casProperties.getAuthn().getJdbc().getQuery();
        QueryJdbcAuthenticationProperties jdbcProperties = jdbc.get(0);

		// casProperties.getCustom() 可获取自定义配置的属性值
        CustomDatabaseAuthenticationHandler handler = new CustomDatabaseAuthenticationHandler(
                CustomDatabaseAuthenticationHandler.class.getSimpleName(),
                servicesManager,
                userService,
                new DefaultPrincipalFactory(),
                casProperties.getCustom(),
                1);
        final Multimap<String, Object> multiMapAttributes = CoreAuthenticationUtils.transformPrincipalAttributesListIntoMultiMap(jdbcProperties.getPrincipalAttributeList());
        final Map<String, Object> attributes = CollectionUtils.wrap(multiMapAttributes);
        handler.setPrincipalAttributeMap(attributes);
        handler.setPasswordEncoder(PasswordEncoderUtils.newPasswordEncoder(jdbcProperties.getPasswordEncoder()));

        return handler;
    }

    /**
     * 注册验证器
     * @param plan
     */
    @Override
    public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
         plan.registerAuthenticationHandler(customAuthenticationConfiguration());
    }

(2)编写抽象身份验证处理程序,方便后续集成其它认证方式,将公共的代码写在一个地方。可自定义idap认证等等

package gds.application.cas.authentication.adaptors;


import gds.application.cas.common.constants.CustomWebConstants;
import gds.application.cas.exception.AccountUnusedLockedException;
import gds.application.cas.exception.WeakPasswordWarnException;
import gds.application.cas.model.usercenter.service.UserService;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.configuration.model.support.custom.CasCustomProperties;
import org.apereo.cas.services.ServicesManager;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.security.auth.login.AccountNotFoundException;
import javax.servlet.http.HttpServletRequest;
import java.security.GeneralSecurityException;
import java.util.HashMap;
import java.util.Map;

@Slf4j
@Setter
@Getter
public abstract class AbstractCustomAuthenticationHandler extends AbstractUsernamePasswordAuthenticationHandler {

    /**
     * Mapping of LDAP attribute name to principal attribute name.
     */
    protected Map<String, Object> principalAttributeMap = new HashMap<>();

    public final UserService userService;

    public final CasCustomProperties casCustomProperties;

    public AbstractCustomAuthenticationHandler(String name, ServicesManager servicesManager, UserService userService, PrincipalFactory principalFactory, CasCustomProperties casCustomProperties, Integer order) {
        super(name, servicesManager, principalFactory, order);
        this.userService = userService;
        this.casCustomProperties = casCustomProperties;
    }

    @Override
    protected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {
        final UsernamePasswordCredential originalUserPass = (UsernamePasswordCredential) credential;
        final UsernamePasswordCredential userPass = new UsernamePasswordCredential(originalUserPass.getUsername(), originalUserPass.getPassword());
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		//这里可以自定义在检验密码之前做一些操作
        return beforeAuthenticationUsernamePasswordInternal(userPass, originalUserPass.getPassword());
    }

    protected abstract AuthenticationHandlerExecutionResult beforeAuthenticationUsernamePasswordInternal(UsernamePasswordCredential credential,
                                                                                                 String originalPassword) throws GeneralSecurityException, PreventedException;
}

(3)编写身份验证处理程序

package gds.application.cas.authentication.adaptors;

import gds.application.cas.common.constants.CustomWebConstants;
import gds.application.cas.common.utils.ObjectUtils;
import gds.application.cas.exception.AccountUnusedLockedException;
import gds.application.cas.exception.MustChangePasswordException;
import gds.application.cas.exception.WeakPasswordWarnException;
import gds.application.cas.model.usercenter.service.UserService;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.authentication.AuthenticationHandlerExecutionResult;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.exceptions.AccountDisabledException;
import org.apereo.cas.authentication.principal.Principal;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.configuration.model.support.custom.CasCustomProperties;
import org.apereo.cas.services.ServicesManager;
import org.apereo.cas.util.CollectionUtils;
import org.ldaptive.LdapEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.security.auth.login.FailedLoginException;
import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletRequest;
import java.security.GeneralSecurityException;
import java.util.*;

/**
 * 自定义登录验证器
 *
 * @author dongqifei
 */
@Slf4j
@Setter
public class CustomDatabaseAuthenticationHandler extends AbstractCustomAuthenticationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(CustomDatabaseAuthenticationHandler.class);

    private final String FIELDPASSWORD = "password";

    public CustomDatabaseAuthenticationHandler(String name, ServicesManager servicesManager, UserService userService, PrincipalFactory principalFactory, CasCustomProperties casCustomProperties, Integer order) {
        super(name, servicesManager, userService, principalFactory, casCustomProperties, order);
    }

    @Override
    protected AuthenticationHandlerExecutionResult beforeAuthenticationUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        final Map<String, Object> attributeMap = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

        final String username = credential.getUsername();
        final String password = credential.getPassword();

        try {
            Map<String, Object> dbFields;
            try{
                dbFields = ObjectUtils.objectToMap(this.getUserService().selectByPrimaryKey(username));
            }catch (Exception ex) {
                throw new FailedLoginException("DTO转换Map异常");
            }

            final String dbPassword = (String) dbFields.get(this.FIELDPASSWORD);
            if ((StringUtils.isNotBlank(originalPassword) && !matches(originalPassword, dbPassword))
                    || (StringUtils.isBlank(originalPassword) && !StringUtils.equals(password, dbPassword))) {
                throw new FailedLoginException("用户名或密码错误!");
            }

            // 封装用户扩展属性
            final Principal principal = createPrincipal(username, attributeMap);
            return createHandlerResult(credential, principal);
        } catch (FailedLoginException e) {
            LOGGER.error("登录失败[{}]",e.getMessage());
            throw e;
        } 
    }

    /**
     * 如果查询的用户信息包含主体属性,则创建具有属性的CAS主体。
     * @param username
     * @param dbFields
     * @return
     */
    protected Principal createPrincipal(final String username, final Map<String, Object> dbFields){
        final Map<String, Object> attributeMap = collectPrincipalAttributes(dbFields);
        return this.principalFactory.createPrincipal(username, attributeMap);
    }

    /**
     * 封装用户扩展属性
     *
     * @param dbFields 数据源
     */
    private Map<String, Object> collectPrincipalAttributes(final Map<String, Object> dbFields) {
        final Map<String, Object> attributeMap = new LinkedHashMap<>(this.principalAttributeMap.size());
        this.principalAttributeMap.forEach((key, names) -> {
            final Object attribute = dbFields.get(key);
            if (attribute != null) {
                LOGGER.debug("Found attribute [{}] from the query results", key);
                final Collection<String> attributeNames = (Collection<String>) names;
                attributeNames.forEach(s -> {
                    LOGGER.debug("Principal attribute [{}] is virtually remapped/renamed to [{}]", key, s);
                    attributeMap.put(s, CollectionUtils.wrap(attribute.toString()));
                });
            } else {
                LOGGER.warn("Requested attribute [{}] could not be found in the query results", key);
            }
        });
        return attributeMap;
    }
}

(4)spring.factories 中配置

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  gds.application.cas.config.spring.SpringConfig,\
  gds.application.cas.config.principal.CustomDatabaseAuthenticationConfiguration

(5)至此自定义验证器完成。

如果CAS是https协议的话,需要生成证书;
生成证书可参考 https://blog.csdn.net/qq_21359467/article/details/102731032
系统中如果同时存在数据库认证和ldap认证方式,两种将都会被加载,只要有一种认证通过就行

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
server 地址: http://www.jasig.org/cas/download client 地址: http://www.ja-sig.org/downloads/cas-clients/ 当前最新版本 cas-server-3.4.2 , cas-client-3.1.10 2. 安装 server l 解压 cas-server-3.4.2 ,将 cas-server-3.4.2\modules\cas-server-webapp-3.4.2.war 拷贝到 tomcat 的 webapps 下。 3. 配置 server 3.1. 添加 cas server 依赖的 jar n cas-server-3.4.2\modules\cas-server-support-jdbc-3.4.2.jar 、 cas-server-integration-restlet-3.4.2.jar 拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib 目录下。 n 数据库驱动 jar 拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib 目录下。 n 到 apache 网站下载下面三个 cas server 依赖 jar 包拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib 目录下 Ø http://apache.freelamp.com/commons/collections/binaries/commons-collections-3.2.1-bin.zip Ø http://apache.etoak.com/commons/dbcp/binaries/commons-dbcp-1.4-bin.zip Ø http://apache.etoak.com/commons/pool/binaries/commons-pool-1.5.4-bin.zip n 下载 restlet 相关 http://www.restlet.org/downloads/ , 解压后将下面 jar 拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib: ( 它奶奶地这一步骤很折腾 ) com.noelios.restlet.ext.servlet_2.5.jar com.noelios.restlet.ext.spring_2.5.jar com.noelios.restlet.jar org.restlet.ext.spring_2.5.jar org.restlet.jar n 下载 CGlib http://sourceforge.net/projects/cglib/files/ 拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib 。 n 下载 ASM/OW2 http://forge.ow2.org/projects/asm/ 拷贝到 D:\server\apache-tomcat-6.0.18\webapps\cas\WEB-INF\lib 。 3.2. 添加数据源
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值