美文网首页前后端知识交流分享CAS
CAS 5.1.X版本自定义jdbc验证

CAS 5.1.X版本自定义jdbc验证

作者: AaronSimon | 来源:发表于2018-08-11 12:50 被阅读135次

    一、前言

    在不同的项目中,可能由于业务需求或者架构方式的不同,对于用户登录的验证方式也不同。CAS为我们提供了很多的认证模式,其中最常见的认证方式有:

    • JDBC认证,可以通过配置,也可以重写cas相关方法进行自定义认证
    • LDAP认证
    • Basic认证
    • Shrio认证
    • Pac4j认证
    • MongoDB认证
    • Rest认证
    • IP黑白名单
    • 第三方认证:微信,QQ,github等

    在笔者最近接手的项目中,考虑后期验证方式的变化和程序的扩展性,决定重写CAS的验证方法进行自定义验证。另外,对于登录的验证信息,我们除了用户名和密码,可能还需要部门信息,验证码等等,这也需要重写cas的方法。

    二、实战

    2.1 pom文件

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.simon.cas</groupId>
        <artifactId>cas_boot</artifactId>
        <version>2.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
    
        <name>cas_boot</name>
        <description>cas服务端</description>
    
        <properties>
            <cas.version>5.1.5</cas.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <springboot.version>2.0.2.RELEASE</springboot.version>
            <spring-cloud.version>Finchley.SR1</spring-cloud.version>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <!--cas-->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-webapp-tomcat</artifactId>
                <version>${cas.version}</version>
                <!--这里必须添加type war-->
                <type>war</type>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-configuration</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <!--新增支持服务注册-->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-eureka-client</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <!--新增支持jdbc验证-->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-jdbc</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <!-- 若不想找驱动可以直接写下面的依赖即可,其中包括HSQLDB、Oracle、MYSQL、PostgreSQL、MariaDB、Microsoft SQL Server -->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-jdbc-drivers</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <!--自定义认证,重写Credential-->
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-core-webflow</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-webapp-config</artifactId>
                <version>${cas.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.apereo.cas</groupId>
                <artifactId>cas-server-support-validation</artifactId>
                <version>${cas.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-dbcp2</artifactId>
                <version>2.3.0</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <scope>provided</scope>
                <version>3.1.0</version>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.apereo.cas</groupId>
                    <artifactId>cas-server-support-bom</artifactId>
                    <version>${cas.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>com.rimerosolutions.maven.plugins</groupId>
                    <artifactId>wrapper-maven-plugin</artifactId>
                    <version>0.0.4</version>
                    <configuration>
                        <verifyDownload>true</verifyDownload>
                        <checksumAlgorithm>MD5</checksumAlgorithm>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>${springboot.version}</version>
                    <configuration>
                        <mainClass>org.springframework.boot.loader.WarLauncher</mainClass>
                        <addResources>true</addResources>
                        <executable>false</executable>
                        <layout>WAR</layout>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>repackage</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.1.0</version>
                    <configuration>
                        <warName>cas</warName>
                        <failOnMissingWebXml>false</failOnMissingWebXml>
                        <recompressZippedFiles>false</recompressZippedFiles>
                        <archive>
                            <compress>false</compress>
                            <manifestFile>${project.build.directory}/war/work/org.apereo.cas/cas-server-webapp-tomcat/META-INF/MANIFEST.MF</manifestFile>
                        </archive>
                        <overlays>
                            <overlay>
                                <groupId>org.apereo.cas</groupId>
                                <artifactId>cas-server-webapp-tomcat</artifactId>
                            </overlay>
                        </overlays>
                        <!-- <dependentWarExcludes> -->
                            <!--&lt;!&ndash;war包下的服务不进行初始化&ndash;&gt;-->
                            <!--**/services/*.json-->
                        <!--</dependentWarExcludes>-->
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.3</version>
                </plugin>
            </plugins>
            <finalName>cas</finalName>
        </build>
    
        <repositories>
            <repository>
                <id>spring-snapshots</id>
                <name>Spring Snapshots</name>
                <url>https://repo.spring.io/snapshot</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>spring-milestones</id>
                <name>Spring Milestones</name>
                <url>https://repo.spring.io/milestone</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>sonatype-releases</id>
                <name>sonatype-releases</name>
                <url>https://oss.sonatype.org/content/repositories/releases/</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>sonatype-snapshots</id>
                <name>sonatype-snapshots</name>
                <url>http://oss.sonatype.org/content/repositories/snapshots/</url>
                <snapshots>
                    <enabled>true</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>shibboleth-releases</id>
                <name>shibboleth-releases</name>
                <url>https://build.shibboleth.net/nexus/content/repositories/releases</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>jasig-dev-legacy</id>
                <name>jasig-dev-legacy</name>
                <url>http://developer.jasig.org/repo/content/groups/m2-legacy</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>duo-uniconiam</id>
                <name>duo-uniconiam</name>
                <url>https://dl.bintray.com/uniconiam/maven</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
            <repository>
                <id>jitpack</id>
                <name>jitack</name>
                <url>https://jitpack.io</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
        </repositories>
    </project>
    

    2.2 配置文件

    服务的配置文件统一使用Spring Cloud config配置中心进行管理。

    bootstrap.properties

    #日志目录
    logging.file=logs/cas.log
    #服务名
    spring.application.name=cas
    #从配置中心获取cas-dev.properties配置
    spring.profiles.active=dev
    #分支
    #spring.cloud.config.label=master
    
    #注册中心
    eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka-server/eureka
    #通过服务名获取配置中心
    #spring.cloud.config.discovery.enabled=true
    #spring.cloud.config.discovery.serviceId=config-server
    #spring.cloud.config.failFast=true
    spring.cloud.config.uri = http://localhost:8080/config-server
    #获取配置失败快速响应
    spring.cloud.config.failFast=true
    

    cas-dev.properties

    ##
    # CAS服务上下文配置
    #
    #访问路径
    server.context-path=/cas
    #访问端口
    server.port=8080
    
    #关闭ssl
    server.ssl.enabled=false
    #设定持有SSL certificate的key store的路径
    #server.ssl.key-store=classpath:tomcat.keystore
    #设定访问key store的密码
    #server.ssl.key-store-password=123456
    #server.ssl.keyAlias=passport.sso.com
    # server.ssl.ciphers=
    # server.ssl.client-auth=
    
    # server.ssl.key-alias=
    # server.ssl.key-store-provider=
    # server.ssl.key-store-type=
    # server.ssl.protocol=
    # server.ssl.trust-store=
    # server.ssl.trust-store-password=
    # server.ssl.trust-store-provider=
    # server.ssl.trust-store-type=
    
    #解决http下登录状态不互通
    cas.tgc.secure=false
    cas.warningCookie.secure=false
    
    #允许发出退出控制退出后转发url
    cas.logout.followServiceRedirects=true
    
    
    server.max-http-header-size=2097152
    server.use-forward-headers=true
    server.connection-timeout=20000
    server.error.include-stacktrace=NEVER
    #设定http header的最小值,默认: 0
    server.tomcat.max-http-post-size=2097152
    #设定Tomcat的base 目录,如果没有指定则使用临时目录
    server.tomcat.basedir=build/tomcat
    #是否开启access log,默认: false
    server.tomcat.accesslog.enabled=true
    #设定access logs的格式,默认: common
    server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
    #设定Log 文件的前缀,默认: access_log
    server.tomcat.accesslog.suffix=.log
    #设定tomcat的最大工作线程数,默认为: 0
    server.tomcat.max-threads=10
    #设定http header使用的,用来覆盖原来port的value
    server.tomcat.port-header=X-Forwarded-Port
    #设定Header包含的协议,通常是 X-Forwarded-Proto,如果remoteIpHeader有值,则将设置为RemoteIpValve.
    server.tomcat.protocol-header=X-Forwarded-Proto
    #设定使用SSL的header的值,默认https.
    server.tomcat.protocol-header-https-value=https
    #设定remote IP的header,如果remoteIpHeader有值,则设置为RemoteIpValve
    server.tomcat.remote-ip-header=X-FORWARDED-FOR
    #设定URI的解码字符集.
    server.tomcat.uri-encoding=UTF-8
    
    spring.http.encoding.charset=UTF-8
    spring.http.encoding.enabled=true
    spring.http.encoding.force=true
    ##########################################################JDBC验证##############################################################################
    ###
    ## Query Database Authentication 数据库查询校验用户名开始
    ##
    ##查询账号密码sql,必须包含密码字段,根据sql给予用户名进行查询根据密码字段进行鉴定
    #cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
    ##指定上面的sql查询字段名(必须)
    #cas.authn.jdbc.query[0].fieldPassword=password
    ##指定过期字段,1为过期,若过期不可用
    #cas.authn.jdbc.query[0].fieldExpired=expired
    ##为不可用字段,1为不可用,需要修改密码
    #cas.authn.jdbc.query[0].fieldDisabled=disabled
    ##数据库方言hibernate的知识
    #cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.HSQLDialect
    ##数据库驱动
    #cas.authn.jdbc.query[0].driverClass=org.hsqldb.jdbcDriver
    ##数据库连接
    #cas.authn.jdbc.query[0].url=jdbc:hsqldb:mem:cas-hsql-database
    ##数据库用户名
    #cas.authn.jdbc.query[0].user=sa
    ##数据库密码
    #cas.authn.jdbc.query[0].password=
    ##默认加密策略,通过encodingAlgorithm来指定算法,默认NONE不加密
    #cas.authn.jdbc.query[0].passwordEncoder.type=DEFAULT
    #cas.authn.jdbc.query[0].passwordEncoder.characterEncoding=UTF-8
    #cas.authn.jdbc.query[0].passwordEncoder.encodingAlgorithm=MD5
    #Query Database Authentication 数据库查询校验用户名结束
    
    ##
    #Encode Database Authentication 编码加密开始
    #   对密码进行盐值处理再加密,增加了反查难度
    #
    #加密次数
    #cas.authn.jdbc.encode[0].numberOfIterations=2
    #该列名的值可替代上面的值,但对密码加密时必须取该值进行处理
    #cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
    # 盐值固定列
    #cas.authn.jdbc.encode[0].saltFieldName=username
    #静态盐值
    #cas.authn.jdbc.encode[0].staticSalt=.
    #cas.authn.jdbc.encode[0].sql=select * from sys_user_encode where username=?
    #对处理盐值后的算法
    #cas.authn.jdbc.encode[0].algorithmName=MD5
    #cas.authn.jdbc.encode[0].passwordFieldName=password
    #cas.authn.jdbc.encode[0].expiredFieldName=expired
    #cas.authn.jdbc.encode[0].disabledFieldName=disabled
    #cas.authn.jdbc.encode[0].url=jdbc:hsqldb:mem:cas-hsql-database
    #cas.authn.jdbc.encode[0].dialect=org.hibernate.dialect.HSQLDialect
    #cas.authn.jdbc.encode[0].user=sa
    #cas.authn.jdbc.encode[0].password=
    #cas.authn.jdbc.encode[0].driverClass=org.hsqldb.jdbcDriver
    #Encode Database Authentication 编码加密结束
    
    ##########################################################Shrio验证##############################################################################
    # Shiro Authentication 开始
    #允许登录的用户,必须要有以下权限,否则拒绝,多个逗号隔开
    #cas.authn.shiro.requiredPermissions=staff
    #允许登录的用户,必须要有以下权限,否则拒绝,多个逗号隔开
    #cas.authn.shiro.requiredRoles=admin
    #shiro配置文件位置
    #cas.authn.shiro.config.location=classpath:shiro.ini
    #shiro name 唯一
    #cas.authn.shiro.name=cas-shiro
    # 与Query Authentication一致的加密策略
    #cas.authn.shiro.passwordEncoder.type=DEFAULT
    # 使用MD5加密
    #cas.authn.shiro.passwordEncoder.encodingAlgorithm=MD5
    # Shiro Authentication 结束
    
    ##########################################################REST 认证##############################################################################
    #REST 认证开始
    #请求远程调用接口
    #cas.authn.rest.uri=http://localhost:8101/login
    #加密策略
    #cas.authn.rest.passwordEncoder.type=DEFAULT
    #cas.authn.rest.passwordEncoder.characterEncoding=UTF-8
    #加密算法
    #cas.authn.rest.passwordEncoder.encodingAlgorithm=MD5
    #REST 结束
    
    #设置默认主题 cas5.1.0-cas5.1.6版本无法使用默认主题,需要覆盖
    #cas.theme.defaultThemeName=100000
    
    ##
    # CAS Cloud Bus Configuration
    #
    spring.cloud.bus.enabled=false
    # spring.cloud.bus.refresh.enabled=true
    # spring.cloud.bus.env.enabled=true
    # spring.cloud.bus.destination=CasCloudBus
    # spring.cloud.bus.ack.enabled=true
    
    endpoints.enabled=false
    endpoints.sensitive=true
    
    endpoints.restart.enabled=false
    endpoints.shutdown.enabled=false
    
    management.security.enabled=true
    management.security.roles=ACTUATOR,ADMIN
    management.security.sessions=if_required
    management.context-path=/status
    management.add-application-context-header=false
    
    security.basic.authorize-mode=role
    security.basic.enabled=false
    security.basic.path=/cas/status/**
    
    ##
    # CAS Web Application Session Configuration
    #
    server.session.timeout=300
    server.session.cookie.http-only=true
    server.session.tracking-modes=COOKIE
    
    ##
    # CAS Thymeleaf View Configuration
    #
    spring.thymeleaf.encoding=UTF-8
    #禁止页面缓冲
    spring.thymeleaf.cache=false
    spring.thymeleaf.mode=HTML
    #默认是.html
    #spring.thymeleaf.suffix=.jsp
    
    ##
    # CAS Log4j Configuration
    #
    # logging.config=file:/etc/cas/log4j2.xml
    server.context-parameters.isLog4jAutoInitializationDisabled=true
    
    ##
    # CAS AspectJ Configuration
    #
    spring.aop.auto=true
    spring.aop.proxy-target-class=true
    
    ##
    # CAS Authentication Credentials
    #
    #cas.authn.accept.users=casuser::Mellon
    #不容许静态用户
    staticAuthentication=false
    
    #开启识别json文件,默认false
    cas.serviceRegistry.initFromJson=true
    
    #下面所是数据源,让spring boot自动配置,默认cas屏蔽了spring boot自动配置,下面的代码中我们在spring boot扫描的目录中开启这个配置
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://192.168.10.58:3306/bdc_job?useUnicode=true&characterEncoding=utf-8
    spring.datasource.username=root
    spring.datasource.password=Ibase2016
    spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
    
    spring.jpa.database=mysql
    spring.jpa.show-sql=true
    spring.jpa.hibernate.ddl-auto=update
    spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
    
    #连接池配置
    ##初始化连接:连接池启动时创建的初始化连接数量
    spring.datasource.dbcp2.initial-size=5
    #最大活动连接:连接池在同一时间能够分配的最大活动连接的数量, 如果设置为非正数则表示不限制
    spring.datasource.dbcp2.max-active=1000
    #最大空闲连接:连接池中容许保持空闲状态的最大连接数量,超过的空闲连接将被释放,如果设置为负数表示不限制
    spring.datasource.dbcp2.max-idle=100
    #从连接池获取一个连接时,最大的等待时间,设置为-1时,如果没有可用连接,连接池会一直无限期等待,直到获取到连接为止。
    #如果设置为N(毫秒),则连接池会等待N毫秒,等待不到,则抛出异常
    spring.datasource.dbcp2.max-wait-millis=60000
    #通过这个池创建连接的默认自动提交状态。如果不设置,则setAutoCommit 方法将不被调用
    spring.datasource.dbcp2.default-auto-commit=true
    #通过这个池创建连接的默认只读状态。如果不设置,则setReadOnly  方法将不被调用。(部分驱动不支持只读模式,如:Informix)
    spring.datasource.dbcp2.default-read-only=false
    #指明在从池中租借对象时是否要进行验证有效,如果对象验证失败,则对象将从池子释放,然后我们将尝试租借另一个
    spring.datasource.dbcp2.test-on-borrow=true
    
    #自定义属性 这些属性我们在jdbc验证的时候需要
    #用户名,密码查询
    user.password.query.sql = select u.USERID,u.DEVICESTRING as DEVICE_,u.UKEYSTRING as UKEY_ from USERINFO u left join ORGANINFO o on u.USERID=o.RID where u.LOGINNAME = ? and u.USERPASSWORD = ?
    #ukey查询
    user.ukey.query.sql = select u.USERID from USERINFO u left join ORGANINFO o on u.USERID=o.RID where u.UKEYSTRING = ?
    #系统配置查询(通过系统配置服务获取自定义界面的一些配置信息的sql语句)
    syscfg.query.sql = select SVALUE from SYSKEY where CCATALOG = ? and SKEY = ?
    

    2.3 扩展登录信息

    cas默认提供的登录信息往往不能满足我们的需求,在这里我们将增加系统名,验证码作为登录的扩展信息

    继承RememberMeUsernamePasswordCredential扩展登录信息

    public class UsernamePasswordSubSystemCredential extends RememberMeUsernamePasswordCredential {
    
      /**
       * 验证码
       */
      private String captcha;
    
      /**
       * 子系统
       */
      private String system;
    
      /**
       * 获取验证码
       *
       * @return
       *        验证码
       */
      public String getCaptcha() {
        return captcha;
      }
    
      /**
       * 设置验证码
       *
       * @param captcha
       *        验证码
       */
      public void setCaptcha(String captcha) {
        this.captcha = captcha;
      }
    
      /**
       * 获取子系统
       *
       * @return
       *        子系统
       */
      public String getSystem() {
        return system;
      }
    
      /**
       * 设置子系统
       *
       * @param system
       *        子系统
       */
      public void setSystem(String system) {
        this.system = system;
      }
    
    
      /**
       * 计算hash码.
       *
       * 根据规范,一旦重写{@code equals()}就必须重写此{@code hashCode()}方法。
       */
      @Override
      public int hashCode() {
        return new HashCodeBuilder().appendSuper(super.hashCode()).append(this.system).append(this.getCaptcha()).toHashCode();
      }
    }
    

    将扩展的登录信息绑定到登录的webflow流程中

    public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
      /**
       * 注入系统配置查询bean
       */
      @Autowired
      private SysConfigQueryService sysConfigQueryService;
    
      /**
       * 构造函数
       *
       * @param flowBuilderServices
       * @param loginFlowDefinitionRegistry
       */
      public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry loginFlowDefinitionRegistry) {
        super(flowBuilderServices, loginFlowDefinitionRegistry);
      }
    
      @Override
      protected void doInitialize() throws Exception {
        Flow flow = getLoginFlow();
        bindCredential(flow);
      }
    
      /**
       * 绑定输入信息
       *
       * @param flow
       *          流程
       */
      protected void bindCredential(Flow flow){
        //重写绑定自定义credential
        createFlowVariable(flow,CasWebflowConstants.VAR_ID_CREDENTIAL,UsernamePasswordSubSystemCredential.class);
        //登录页绑定新参数
        final ViewState state = (ViewState)flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
    
        final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
        //由于用户名以及密码已经绑定,所以只需对新加系统参数绑定即可
        //参数1 :字段名
        //参数2 :转换器
        //参数3 :是否必须的字段
        cfg.addBinding(new BinderConfiguration.Binding("system", null, false));
        cfg.addBinding(new BinderConfiguration.Binding("captcha",null,false));
    
        //进入viewstate前传入参数(在系统参数绑定之后设置)
        ActionList entryActionList = state.getEntryActionList();
        entryActionList.add(createEvaluateAction("viewScope.newReg="+sysConfigQueryService.getNewRegister()));
        entryActionList.add(createEvaluateAction("viewScope.pwdReset="+sysConfigQueryService.getResetPassword()));
        entryActionList.add(createEvaluateAction("viewScope.codeValidate="+sysConfigQueryService.getImageValidate()));
        //系统名称
        entryActionList.add(createEvaluateAction("viewScope.sysTitle="+sysConfigQueryService.getSysTitle()));
      }
    }
    

    在上面的代码中,我们除了绑定新添加的登录信息,还设置entryActionList(这个是Spring webflow的一个自定义切点,表示进入某个 state 之后,做其他事情之前,执行相关业务逻辑。Spring Web Flow 共定义了5个切入点,具体可以查看https://docs.spring.io/spring-webflow/docs/2.5.0.RELEASE/reference/html/el.html#el-variable-viewScope ),这样在页面上可以通过${newReg},${pwdReset}...获取传入的相关信息来进行有一些逻辑操作。需要注意的是上面代码绑定新登录信息和设置entryActionList的顺序不能改变,否则在验证的时候无法获取新增的登录信息。另外,SysConfigQueryService的代码也不贴出来了,这里主要就是通过jdbcTemplate查询系统配置信息。

    注册修改后的登录流程

    @Configuration("customerAuthWebflowConfiguration")
    @EnableConfigurationProperties(value = CasConfigurationProperties.class)
    @AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
    public class CustomerAuthWebflowConfiguration {
    
      @Autowired
      @Qualifier("logoutFlowRegistry")
      private FlowDefinitionRegistry logoutFlowRegitry;
    
      @Autowired
      @Qualifier("loginFlowRegistry")
      private FlowDefinitionRegistry loginFlowRegistry;
    
      @Autowired
      @Qualifier("builder")
      private FlowBuilderServices builder;
    
      @Bean
      public CasWebflowConfigurer customWebflowConfigurer() {
        final CustomWebflowConfigurer customWebflowConfigurer = new CustomWebflowConfigurer(builder, loginFlowRegistry);
        customWebflowConfigurer.setLogoutFlowDefinitionRegistry(logoutFlowRegitry);
        return customWebflowConfigurer;
      }
    }
    

    2.4 自定义验证

    这里我们使用jdbc进行验证,通过用户登录信息使用jdbcTemplate进行查询。

    继承AbstractPreAndPostProcessingAuthenticationHandler进行自定义认证

    public class UsernamePasswordSystemAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
    
      /**
       * 为了符合cacheService key的名称定义格式。
       */
      public static final String CSUFFIX_USERLOGIN = "_2ae941e8-21e5-4013-9568-4dcee975333a";
    
      /**
       * 根据用户名和密码查询sql语句
       */
      @Value("${user.password.query.sql}")
      private String userQuerySql;
    
      /**
       * 根据ukey查询sql语句
       */
      @Value("${user.ukey.query.sql}")
      private String uKeyQuerySql;
    
      /**
       * jdbc模板
       */
      @Autowired
      private JdbcTemplate jdbcTemplate;
    
      /**
       * 构造函数
       *
       * @param name
       * @param servicesManager
       * @param principalFactory
       * @param order
       */
      public UsernamePasswordSystemAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
        super(name, servicesManager, principalFactory, order);
      }
    
    
      @Override
      protected HandlerResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
        final UsernamePasswordSubSystemCredential upssc = (UsernamePasswordSubSystemCredential) credential;
        final String username = upssc.getUsername();
        //这里我省略了jdbc验证代码,大家可以自己去实现
        if(username.equals("admin")){
          return createHandlerResult(upssc, this.principalFactory.createPrincipal(upssc.getUsername(),Collections.emptyMap()), null);
        }
        return null;
      }
    
      @Override
      public boolean supports(Credential credential) {
        return credential instanceof UsernamePasswordSubSystemCredential;
      }
    }
    

    注册自定义验证器

    @Import({DataSourceAutoConfiguration.class})
    @Configuration("customAuthenticationEventExecutionPlanConfiguration")
    @EnableConfigurationProperties(CasConfigurationProperties.class)
    public class CustomAuthenticationEventExecutionPlanConfiguration implements AuthenticationEventExecutionPlanConfigurer {
      @Autowired
      @Qualifier("servicesManager")
      private ServicesManager servicesManager;
    
      @Autowired
      @Qualifier("jdbcPrincipalFactory")
      public PrincipalFactory jdbcPrincipalFactory;
    
    
      /**
       * 注册验证器
       *
       * @return
       */
      @Bean
      public AuthenticationHandler customAuthenticationHandler() {
        //优先验证
        return new UsernamePasswordSystemAuthenticationHandler
                ("customAuthenticationHandler", servicesManager,new DefaultPrincipalFactory(),1);
      }
    
      //注册自定义认证器
      @Override
      public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
        plan.registerAuthenticationHandler(customAuthenticationHandler());
      }
    }
    

    我们在代码中增加了@Import({DataSourceAutoConfiguration.class}),开启Spring Boot数据源自动配置,这样才可以使用使用jdbcTemplate。为什么在这里配置的,因为我会将个类配置在Spring Boot的自动配置扫描目录,当然你也可以配置在其它地方,保证其被扫描到就可以。

    2.5 其它Bean配置

    为了在自定义验证的类中注入其它bean执行验证逻辑,例如:注入缓冲bean,实现缓冲逻辑;注入系统配置bean,获取系统配置信息等,我们需要在这里式配置相关的bean,使其能被Spring Boot扫描到。

    @Configuration
    public class BeansConfiguration {
      @Value("${syscfg.query.sql}")
      private String syscfgQuerySql;
    
      @Autowired
      private JdbcTemplate jdbcTemplate;
    
      @Bean
      SysConfigQueryService sysConfigQueryService(){
        return new SysConfigQueryService(syscfgQuerySql,jdbcTemplate);
      }
    
      @Bean
      ICacheService cacheService(){
        return new CacheService();
      }
    

    2.6 配置spring.factories

    SpringFactoriesLoader属于Spring框架私有的一种扩展方案,其主要功能就是从指定的配置文件META-INF/spring.factories加载配置。通过这个方法,我们就能将自定义的认证方法让spring boot 自动配置扫描到。

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.simo.cas.config.CustomerAuthWebflowConfiguration,com.simon.cas.config.CustomAuthenticationEventExecutionPlanConfiguration,com.simon.cas.config.BeansConfiguration
    

    三、登录页面

    <!DOCTYPE html>
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8"/>
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
        <title th:text="${sysTitle}">这里使用了传递进来的属性</title>
        <link rel="stylesheet" th:href="@{/themes/hzww/css/hzww.css}"/>
        <script th:src="@{/themes/common/js/sha1.min.js}"></script>
        <script th:src="@{/themes/common/js/jquery-1.x.min.js}"></script>
        <script th:src="@{/themes/common/js/login.min.js}"></script>
        <script th:src="@{/themes/common/js/jquery.cookie.min.js}"></script>
    </head>
    <body>
    <div id="content">
        <img class="bgimg" alt="" th:src="@{/themes/hzww/images/ibase_loginBg_01.jpg}"/>
        <div class="header">
            <div class="header_left">
                <img th:src="@{/themes/hzww/images/Login_logo.png}">
    
                <p th:text="${sysTitle}">这里使用了传递进来的属性</p>
            </div>
        </div>
        <div class="login_center">
            <div class=login-title>
                <h2>欢迎登录</h2>
                <span>Welcome To Login</span>
            </div>
            <div class="login-box">
                <form id="login_form" method="post" th:object="${credential}">
                    <div class="form_radio">
                        <div th:if="${#fields.hasErrors('*')}" class="errDiv">
                            <span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/>
                        </div>
                    </div>
                    <div class="login_txtBorder" th:unless="${openIdLocalId}">
                        <input type = "text" id="login_user" name="username" th:disabled="${guaEnabled}" th:field="*{username}"  placeholder='登录账户'/>
                    </div>
                    <div class="login_txtBorder">
                        <input type="password" id="login_pw"  name="password" th:field="*{password}"  placeholder='请输入密码'>
                    </div>
                    <div class="login_captchaBorder>
                        <input type="text" id="login_captcha"  name="captcha" th:field="*{captcha}" placeholder='请输入右方验证码' autocomplete="off">
                        <div class="login_captcha_img"><img id="cimg" src="" onclick="" title="看不清?点击更换另一个。"/></div>
                    </div>
    
    
                    <input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
                    <input type="hidden" name="_eventId" value="submit"/>
                    <input type="hidden" name="geolocation"/>
                    <input type="hidden" id="system" name="system" th:field="*{system}">
                    <input id="login_submit" type="submit" class="normal_input" th:value="#{screen.welcome.button.login}" value="登录">
                </form>
            </div>
        </div>
    </div>
    </body>
    
    <script type="text/javascript" th:inline="javascript">
    
    </script>
    </html>
    
    

    相关文章

      网友评论

        本文标题:CAS 5.1.X版本自定义jdbc验证

        本文链接:https://www.haomeiwen.com/subject/krcdbftx.html