一晌柬、前言
在不同的項(xiàng)目中质欲,可能由于業(yè)務(wù)需求或者架構(gòu)方式的不同共郭,對(duì)于用戶登錄的驗(yàn)證方式也不同祠丝。CAS為我們提供了很多的認(rèn)證模式,其中最常見(jiàn)的認(rèn)證方式有:
- JDBC認(rèn)證除嘹,可以通過(guò)配置写半,也可以重寫cas相關(guān)方法進(jìn)行自定義認(rèn)證
- LDAP認(rèn)證
- Basic認(rèn)證
- Shrio認(rèn)證
- Pac4j認(rèn)證
- MongoDB認(rèn)證
- Rest認(rèn)證
- IP黑白名單
- 第三方認(rèn)證:微信,QQ尉咕,github等
在筆者最近接手的項(xiàng)目中叠蝇,考慮后期驗(yàn)證方式的變化和程序的擴(kuò)展性,決定重寫CAS的驗(yàn)證方法進(jìn)行自定義驗(yàn)證年缎。另外悔捶,對(duì)于登錄的驗(yàn)證信息,我們除了用戶名和密碼单芜,可能還需要部門信息蜕该,驗(yàn)證碼等等,這也需要重寫cas的方法洲鸠。
二堂淡、實(shí)戰(zhàn)
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服務(wù)端</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>
<!--新增支持服務(wù)注冊(cè)-->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-eureka-client</artifactId>
<version>${cas.version}</version>
</dependency>
<!--新增支持jdbc驗(yàn)證-->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
<!-- 若不想找驅(qū)動(dòng)可以直接寫下面的依賴即可,其中包括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>
<!--自定義認(rèn)證,重寫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> -->
<!--<!–war包下的服務(wù)不進(jìn)行初始化–>-->
<!--**/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 配置文件
服務(wù)的配置文件統(tǒng)一使用Spring Cloud config配置中心進(jìn)行管理居灯。
bootstrap.properties
#日志目錄
logging.file=logs/cas.log
#服務(wù)名
spring.application.name=cas
#從配置中心獲取cas-dev.properties配置
spring.profiles.active=dev
#分支
#spring.cloud.config.label=master
#注冊(cè)中心
eureka.client.serviceUrl.defaultZone=http://localhost:8080/eureka-server/eureka
#通過(guò)服務(wù)名獲取配置中心
#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
#獲取配置失敗快速響應(yīng)
spring.cloud.config.failFast=true
cas-dev.properties
##
# CAS服務(wù)上下文配置
#
#訪問(wèn)路徑
server.context-path=/cas
#訪問(wèn)端口
server.port=8080
#關(guān)閉ssl
server.ssl.enabled=false
#設(shè)定持有SSL certificate的key store的路徑
#server.ssl.key-store=classpath:tomcat.keystore
#設(shè)定訪問(wèn)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下登錄狀態(tài)不互通
cas.tgc.secure=false
cas.warningCookie.secure=false
#允許發(fā)出退出控制退出后轉(zhuǎn)發(fā)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
#設(shè)定http header的最小值祭务,默認(rèn): 0
server.tomcat.max-http-post-size=2097152
#設(shè)定Tomcat的base 目錄内狗,如果沒(méi)有指定則使用臨時(shí)目錄
server.tomcat.basedir=build/tomcat
#是否開(kāi)啟access log,默認(rèn): false
server.tomcat.accesslog.enabled=true
#設(shè)定access logs的格式义锥,默認(rèn): common
server.tomcat.accesslog.pattern=%t %a "%r" %s (%D ms)
#設(shè)定Log 文件的前綴柳沙,默認(rèn): access_log
server.tomcat.accesslog.suffix=.log
#設(shè)定tomcat的最大工作線程數(shù),默認(rèn)為: 0
server.tomcat.max-threads=10
#設(shè)定http header使用的拌倍,用來(lái)覆蓋原來(lái)port的value
server.tomcat.port-header=X-Forwarded-Port
#設(shè)定Header包含的協(xié)議赂鲤,通常是 X-Forwarded-Proto,如果remoteIpHeader有值柱恤,則將設(shè)置為RemoteIpValve.
server.tomcat.protocol-header=X-Forwarded-Proto
#設(shè)定使用SSL的header的值数初,默認(rèn)https.
server.tomcat.protocol-header-https-value=https
#設(shè)定remote IP的header,如果remoteIpHeader有值梗顺,則設(shè)置為RemoteIpValve
server.tomcat.remote-ip-header=X-FORWARDED-FOR
#設(shè)定URI的解碼字符集.
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
spring.http.encoding.force=true
##########################################################JDBC驗(yàn)證##############################################################################
###
## Query Database Authentication 數(shù)據(jù)庫(kù)查詢校驗(yàn)用戶名開(kāi)始
##
##查詢賬號(hào)密碼sql泡孩,必須包含密碼字段,根據(jù)sql給予用戶名進(jìn)行查詢根據(jù)密碼字段進(jìn)行鑒定
#cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
##指定上面的sql查詢字段名(必須)
#cas.authn.jdbc.query[0].fieldPassword=password
##指定過(guò)期字段,1為過(guò)期寺谤,若過(guò)期不可用
#cas.authn.jdbc.query[0].fieldExpired=expired
##為不可用字段仑鸥,1為不可用,需要修改密碼
#cas.authn.jdbc.query[0].fieldDisabled=disabled
##數(shù)據(jù)庫(kù)方言hibernate的知識(shí)
#cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.HSQLDialect
##數(shù)據(jù)庫(kù)驅(qū)動(dòng)
#cas.authn.jdbc.query[0].driverClass=org.hsqldb.jdbcDriver
##數(shù)據(jù)庫(kù)連接
#cas.authn.jdbc.query[0].url=jdbc:hsqldb:mem:cas-hsql-database
##數(shù)據(jù)庫(kù)用戶名
#cas.authn.jdbc.query[0].user=sa
##數(shù)據(jù)庫(kù)密碼
#cas.authn.jdbc.query[0].password=
##默認(rèn)加密策略变屁,通過(guò)encodingAlgorithm來(lái)指定算法眼俊,默認(rèn)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 數(shù)據(jù)庫(kù)查詢校驗(yàn)用戶名結(jié)束
##
#Encode Database Authentication 編碼加密開(kāi)始
# 對(duì)密碼進(jìn)行鹽值處理再加密,增加了反查難度
#
#加密次數(shù)
#cas.authn.jdbc.encode[0].numberOfIterations=2
#該列名的值可替代上面的值粟关,但對(duì)密碼加密時(shí)必須取該值進(jìn)行處理
#cas.authn.jdbc.encode[0].numberOfIterationsFieldName=
# 鹽值固定列
#cas.authn.jdbc.encode[0].saltFieldName=username
#靜態(tài)鹽值
#cas.authn.jdbc.encode[0].staticSalt=.
#cas.authn.jdbc.encode[0].sql=select * from sys_user_encode where username=?
#對(duì)處理鹽值后的算法
#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 編碼加密結(jié)束
##########################################################Shrio驗(yàn)證##############################################################################
# Shiro Authentication 開(kāi)始
#允許登錄的用戶疮胖,必須要有以下權(quán)限,否則拒絕誊役,多個(gè)逗號(hào)隔開(kāi)
#cas.authn.shiro.requiredPermissions=staff
#允許登錄的用戶获列,必須要有以下權(quán)限谷市,否則拒絕蛔垢,多個(gè)逗號(hào)隔開(kāi)
#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 結(jié)束
##########################################################REST 認(rèn)證##############################################################################
#REST 認(rèn)證開(kāi)始
#請(qǐng)求遠(yuǎn)程調(diào)用接口
#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 結(jié)束
#設(shè)置默認(rèn)主題 cas5.1.0-cas5.1.6版本無(wú)法使用默認(rèn)主題,需要覆蓋
#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
#禁止頁(yè)面緩沖
spring.thymeleaf.cache=false
spring.thymeleaf.mode=HTML
#默認(rèn)是.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
#不容許靜態(tài)用戶
staticAuthentication=false
#開(kāi)啟識(shí)別json文件迫悠,默認(rèn)false
cas.serviceRegistry.initFromJson=true
#下面所是數(shù)據(jù)源鹏漆,讓spring boot自動(dòng)配置,默認(rèn)cas屏蔽了spring boot自動(dòng)配置创泄,下面的代碼中我們?cè)趕pring boot掃描的目錄中開(kāi)啟這個(gè)配置
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
#連接池配置
##初始化連接:連接池啟動(dòng)時(shí)創(chuàng)建的初始化連接數(shù)量
spring.datasource.dbcp2.initial-size=5
#最大活動(dòng)連接:連接池在同一時(shí)間能夠分配的最大活動(dòng)連接的數(shù)量, 如果設(shè)置為非正數(shù)則表示不限制
spring.datasource.dbcp2.max-active=1000
#最大空閑連接:連接池中容許保持空閑狀態(tài)的最大連接數(shù)量,超過(guò)的空閑連接將被釋放,如果設(shè)置為負(fù)數(shù)表示不限制
spring.datasource.dbcp2.max-idle=100
#從連接池獲取一個(gè)連接時(shí)艺玲,最大的等待時(shí)間,設(shè)置為-1時(shí),如果沒(méi)有可用連接鞠抑,連接池會(huì)一直無(wú)限期等待饭聚,直到獲取到連接為止。
#如果設(shè)置為N(毫秒)搁拙,則連接池會(huì)等待N毫秒秒梳,等待不到法绵,則拋出異常
spring.datasource.dbcp2.max-wait-millis=60000
#通過(guò)這個(gè)池創(chuàng)建連接的默認(rèn)自動(dòng)提交狀態(tài)。如果不設(shè)置酪碘,則setAutoCommit 方法將不被調(diào)用
spring.datasource.dbcp2.default-auto-commit=true
#通過(guò)這個(gè)池創(chuàng)建連接的默認(rèn)只讀狀態(tài)朋譬。如果不設(shè)置,則setReadOnly 方法將不被調(diào)用兴垦。(部分驅(qū)動(dòng)不支持只讀模式徙赢,如:Informix)
spring.datasource.dbcp2.default-read-only=false
#指明在從池中租借對(duì)象時(shí)是否要進(jìn)行驗(yàn)證有效,如果對(duì)象驗(yàn)證失敗探越,則對(duì)象將從池子釋放狡赐,然后我們將嘗試租借另一個(gè)
spring.datasource.dbcp2.test-on-borrow=true
#自定義屬性 這些屬性我們?cè)趈dbc驗(yàn)證的時(shí)候需要
#用戶名,密碼查詢
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 = ?
#系統(tǒng)配置查詢(通過(guò)系統(tǒng)配置服務(wù)獲取自定義界面的一些配置信息的sql語(yǔ)句)
syscfg.query.sql = select SVALUE from SYSKEY where CCATALOG = ? and SKEY = ?
2.3 擴(kuò)展登錄信息
cas默認(rèn)提供的登錄信息往往不能滿足我們的需求钦幔,在這里我們將增加系統(tǒng)名阴汇,驗(yàn)證碼作為登錄的擴(kuò)展信息
繼承RememberMeUsernamePasswordCredential
擴(kuò)展登錄信息
public class UsernamePasswordSubSystemCredential extends RememberMeUsernamePasswordCredential {
/**
* 驗(yàn)證碼
*/
private String captcha;
/**
* 子系統(tǒng)
*/
private String system;
/**
* 獲取驗(yàn)證碼
*
* @return
* 驗(yàn)證碼
*/
public String getCaptcha() {
return captcha;
}
/**
* 設(shè)置驗(yàn)證碼
*
* @param captcha
* 驗(yàn)證碼
*/
public void setCaptcha(String captcha) {
this.captcha = captcha;
}
/**
* 獲取子系統(tǒng)
*
* @return
* 子系統(tǒng)
*/
public String getSystem() {
return system;
}
/**
* 設(shè)置子系統(tǒng)
*
* @param system
* 子系統(tǒng)
*/
public void setSystem(String system) {
this.system = system;
}
/**
* 計(jì)算hash碼.
*
* 根據(jù)規(guī)范,一旦重寫{@code equals()}就必須重寫此{(lán)@code hashCode()}方法节槐。
*/
@Override
public int hashCode() {
return new HashCodeBuilder().appendSuper(super.hashCode()).append(this.system).append(this.getCaptcha()).toHashCode();
}
}
將擴(kuò)展的登錄信息綁定到登錄的webflow流程中
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
/**
* 注入系統(tǒng)配置查詢bean
*/
@Autowired
private SysConfigQueryService sysConfigQueryService;
/**
* 構(gòu)造函數(shù)
*
* @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);
//登錄頁(yè)綁定新參數(shù)
final ViewState state = (ViewState)flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
//由于用戶名以及密碼已經(jīng)綁定搀庶,所以只需對(duì)新加系統(tǒng)參數(shù)綁定即可
//參數(shù)1 :字段名
//參數(shù)2 :轉(zhuǎn)換器
//參數(shù)3 :是否必須的字段
cfg.addBinding(new BinderConfiguration.Binding("system", null, false));
cfg.addBinding(new BinderConfiguration.Binding("captcha",null,false));
//進(jìn)入viewstate前傳入?yún)?shù)(在系統(tǒng)參數(shù)綁定之后設(shè)置)
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()));
//系統(tǒng)名稱
entryActionList.add(createEvaluateAction("viewScope.sysTitle="+sysConfigQueryService.getSysTitle()));
}
}
在上面的代碼中,我們除了綁定新添加的登錄信息铜异,還設(shè)置entryActionList(這個(gè)是Spring webflow的一個(gè)自定義切點(diǎn)哥倔,表示進(jìn)入某個(gè) state 之后,做其他事情之前揍庄,執(zhí)行相關(guān)業(yè)務(wù)邏輯咆蒿。Spring Web Flow 共定義了5個(gè)切入點(diǎn),具體可以查看https://docs.spring.io/spring-webflow/docs/2.5.0.RELEASE/reference/html/el.html#el-variable-viewScope )蚂子,這樣在頁(yè)面上可以通過(guò)${newReg},${pwdReset}...
獲取傳入的相關(guān)信息來(lái)進(jìn)行有一些邏輯操作沃测。需要注意的是上面代碼綁定新登錄信息和設(shè)置entryActionList的順序不能改變,否則在驗(yàn)證的時(shí)候無(wú)法獲取新增的登錄信息食茎。另外蒂破,SysConfigQueryService
的代碼也不貼出來(lái)了,這里主要就是通過(guò)jdbcTemplate查詢系統(tǒng)配置信息别渔。
注冊(cè)修改后的登錄流程
@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 自定義驗(yàn)證
這里我們使用jdbc進(jìn)行驗(yàn)證附迷,通過(guò)用戶登錄信息使用jdbcTemplate進(jìn)行查詢。
繼承AbstractPreAndPostProcessingAuthenticationHandler
進(jìn)行自定義認(rèn)證
public class UsernamePasswordSystemAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
/**
* 為了符合cacheService key的名稱定義格式哎媚。
*/
public static final String CSUFFIX_USERLOGIN = "_2ae941e8-21e5-4013-9568-4dcee975333a";
/**
* 根據(jù)用戶名和密碼查詢sql語(yǔ)句
*/
@Value("${user.password.query.sql}")
private String userQuerySql;
/**
* 根據(jù)ukey查詢sql語(yǔ)句
*/
@Value("${user.ukey.query.sql}")
private String uKeyQuerySql;
/**
* jdbc模板
*/
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 構(gòu)造函數(shù)
*
* @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驗(yàn)證代碼喇伯,大家可以自己去實(shí)現(xiàn)
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;
}
}
注冊(cè)自定義驗(yàn)證器
@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;
/**
* 注冊(cè)驗(yàn)證器
*
* @return
*/
@Bean
public AuthenticationHandler customAuthenticationHandler() {
//優(yōu)先驗(yàn)證
return new UsernamePasswordSystemAuthenticationHandler
("customAuthenticationHandler", servicesManager,new DefaultPrincipalFactory(),1);
}
//注冊(cè)自定義認(rèn)證器
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(customAuthenticationHandler());
}
}
我們?cè)诖a中增加了@Import({DataSourceAutoConfiguration.class})
,開(kāi)啟Spring Boot數(shù)據(jù)源自動(dòng)配置,這樣才可以使用使用jdbcTemplate拨与。為什么在這里配置的稻据,因?yàn)槲視?huì)將個(gè)類配置在Spring Boot的自動(dòng)配置掃描目錄,當(dāng)然你也可以配置在其它地方买喧,保證其被掃描到就可以捻悯。
2.5 其它Bean配置
為了在自定義驗(yàn)證的類中注入其它bean執(zhí)行驗(yàn)證邏輯箩朴,例如:注入緩沖bean,實(shí)現(xiàn)緩沖邏輯秋度;注入系統(tǒng)配置bean,獲取系統(tǒng)配置信息等炸庞,我們需要在這里式配置相關(guān)的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框架私有的一種擴(kuò)展方案,其主要功能就是從指定的配置文件META-INF/spring.factories
加載配置埠居。通過(guò)這個(gè)方法,我們就能將自定義的認(rèn)證方法讓spring boot 自動(dòng)配置掃描到事期。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.simo.cas.config.CustomerAuthWebflowConfiguration,com.simon.cas.config.CustomAuthenticationEventExecutionPlanConfiguration,com.simon.cas.config.BeansConfiguration
三滥壕、登錄頁(yè)面
<!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}">這里使用了傳遞進(jìn)來(lái)的屬性</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}">這里使用了傳遞進(jìn)來(lái)的屬性</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='請(qǐng)輸入密碼'>
</div>
<div class="login_captchaBorder>
<input type="text" id="login_captcha" name="captcha" th:field="*{captcha}" placeholder='請(qǐng)輸入右方驗(yàn)證碼' autocomplete="off">
<div class="login_captcha_img"><img id="cimg" src="" onclick="" title="看不清?點(diǎn)擊更換另一個(gè)兽泣。"/></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>