转载地址: https://blog.youkuaiyun.com/Anumbrella/article/details/82728641
1、下载 CAS
# 文档地址
https://apereo.github.io/cas
# 下载地址
https://github.com/apereo/cas-overlay-template/tree/5.3
# idea 编辑
建立 src/main/java、src/main/resources、src/main/resources/services 文件夹
# 拷贝配置文件
1.拷贝 target/cas/WEB-INF/classes/application.properties 文件到 src/main/resources
2.拷贝 target/cas/WEB-INF/classes/services/HTTPSandIMAPS-10000001.json 文件到 src/main/resources/services,添加 http
2、配置依赖
- pom.xml
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-json-service-registry</artifactId>
<version>${cas.version}</version>
</dependency>
- application.properties
# 开启识别Json文件,默认false
cas.serviceRegistry.initFromJson=true
#自动扫描服务配置,默认开启
cas.serviceRegistry.watcherEnabled=true
#120秒扫描一遍
cas.serviceRegistry.schedule.repeatInterval=120000
#延迟15秒开启
# cas.serviceRegistry.schedule.startDelay=15000
cas.serviceRegistry.json.location=classpath:/services
# 服务重定向
cas.logout.followServiceRedirects=true
3、开启 jdbc 验证
- 配置依赖
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-support-jdbc</artifactId>
<version>${cas.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.34</version>
</dependency>
- 创建数据库
CREATE DATABASE cas;
USE cas;
CREATE TABLE `sys_user` (
`id` VARCHAR(20) NOT NULL,
`username` VARCHAR(100) NOT NULL,
`password` VARCHAR(100) NOT NULL,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB;
- 修改 application.properties
cas.authn.jdbc.query[0].user=root
cas.authn.jdbc.query[0].password=root
cas.authn.jdbc.query[0].driverClass=com.mysql.jdbc.Driver
cas.authn.jdbc.query[0].url=jdbc:mysql://192.168.1.24:3306/cas?useUnicode=true&characterEncoding=utf-8
cas.authn.jdbc.query[0].dialect=org.hibernate.dialect.MySQLDialect
cas.authn.jdbc.query[0].sql=select * from sys_user where username=?
cas.authn.jdbc.query[0].fieldPassword=password
# 0正常,1需要修改密码
cas.authn.jdbc.query[0].fieldExpired=expired
# 0正常,1账户被禁用,无法登陆
cas.authn.jdbc.query[0].fieldDisabled=disabled
- 注释代码
#cas.authn.accept.users=casuser::Mellon
4、添加MD5加密配置
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
5、自定义密码策略
- 密码类
package com.vim.sso;
import org.springframework.security.crypto.password.PasswordEncoder;
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
//charSequence 为用户输入的密码
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String str) {
//charSequence 为用户输入的密码
System.out.println(charSequence);
//str 为数据库密码
System.out.println(str);
return true;
}
}
- application.properties
cas.authn.jdbc.query[0].passwordEncoder.type=com.vim.sso.MyPasswordEncoder
6、自定义密码策略(自定义数据源查询方式)
- 添加依赖
<!-- Custom Authentication -->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-authentication-api</artifactId>
<version>${cas.version}</version>
</dependency>
<!-- Custom Configuration -->
<dependency>
<groupId>org.apereo.cas</groupId>
<artifactId>cas-server-core-configuration-api</artifactId>
<version>${cas.version}</version>
</dependency>
CustomUsernamePasswordAuthentication
package com.vim.sso;
import org.apereo.cas.authentication.*;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.handler.support.AbstractUsernamePasswordAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class CustomUsernamePasswordAuthentication extends AbstractPreAndPostProcessingAuthenticationHandler {
public CustomUsernamePasswordAuthentication(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
public boolean supports(Credential credential) {
//判断传递过来的Credential 是否是自己能处理的类型
return credential instanceof CustomCredential;
}
@Override
protected AuthenticationHandlerExecutionResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
CustomCredential customCredential = (CustomCredential) credential;
String username = customCredential.getUsername();
String password = customCredential.getPassword();
String telephone = customCredential.getTelephone();
System.out.println("******CustomUsernamePasswordAuthentication******:" + username);
System.out.println("******CustomUsernamePasswordAuthentication******:" + password);
System.out.println("******CustomUsernamePasswordAuthentication******:" + telephone);
Map<String, Object> returnInfo = new HashMap<>();
returnInfo.put("aName", "aValue");
returnInfo.put("bName", "bValue");
final List<MessageDescriptor> list = new ArrayList<>();
return createHandlerResult(credential,
this.principalFactory.createPrincipal(username, returnInfo), list);
}
}
- 配置类
package com.vim.sso;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.DefaultPrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration("CustomAuthenticationConfiguration")
public class CustomAuthenticationConfiguration implements AuthenticationEventExecutionPlanConfigurer {
@Autowired
@Qualifier("servicesManager")
private ServicesManager servicesManager;
@Bean
public AuthenticationHandler myAuthenticationHandler() {
// 参数: name, servicesManager, principalFactory, order
// 定义为优先使用它进行认证
return new CustomUsernamePasswordAuthentication(CustomUsernamePasswordAuthentication.class.getName(),
servicesManager, new DefaultPrincipalFactory(), 1);
}
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(myAuthenticationHandler());
}
}
- resources 下创建META-INF文件夹,并创建spring.factories文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.vim.sso.CustomAuthenticationConfiguration
7、自定义登录页面
- 添加依赖
<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-core-webflow-api</artifactId>
<version>${cas.version}</version>
</dependency>
- 注册 json,resources 下创建 services/web-10000001.json,主题名称为 customTheme
{
"@class" : "org.apereo.cas.services.RegexRegisteredService",
"serviceId" : "^(https|imaps|http)://.*",
"name" : "web",
"id" : 10000001,
"evaluationOrder" : 10,
"accessStrategy" : {
"@class" : "org.apereo.cas.services.DefaultRegisteredServiceAccessStrategy",
"enabled" : true,
"ssoEnabled" : true
},
"theme": "customTheme"
}
- application.properties 开启json扫码
# 开启识别Json文件,默认false
cas.serviceRegistry.initFromJson=true
#自动扫描服务配置,默认开启
cas.serviceRegistry.watcherEnabled=true
#120秒扫描一遍
cas.serviceRegistry.schedule.repeatInterval=120000
#延迟15秒开启
# cas.serviceRegistry.schedule.startDelay=15000
cas.serviceRegistry.json.location=classpath:/services
cas.theme.defaultThemeName=customTheme
- resources 下创建customTheme.properties文件
customTheme.javascript.file=/themes/customTheme/js/cas.js
customTheme.standard.css.file=/themes/customTheme/css/cas.css
customTheme.login.images.path=/themes/customTheme/images
cas.standard.css.file=/css/cas.css
cas.javascript.file=/js/cas.js
cas.admin.css.file=/css/admin.css
- 创建静态资源和登录文件
# 静态资源文件
resources/static/themes/customTheme/css/cas.css
resources/static/themes/customTheme/js/cas.js
resources/static/themes/customTheme/images/
# 登录页面
resources/templates/customTheme/casLoginView.html
8、添加自定义字段
- CustomWebflowConfigurer
package com.vim.sso;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConstants;
import org.apereo.cas.web.flow.configurer.AbstractCasWebflowConfigurer;
import org.springframework.context.ApplicationContext;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.Flow;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
/**
* @author anumbrella
*/
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices,
FlowDefinitionRegistry flowDefinitionRegistry,
ApplicationContext applicationContext,
CasConfigurationProperties casProperties) {
super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);
}
@Override
protected void doInitialize() {
final Flow flow = super.getLoginFlow();
bindCredential(flow);
}
/**
* 绑定自定义的Credential信息
*
* @param flow
*/
protected void bindCredential(Flow flow) {
// 重写绑定自定义credential
// 重写绑定自定义credential
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, CustomCredential.class);
// 登录页绑定新参数
final ViewState state = (ViewState) flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
// 由于用户名以及密码已经绑定,所以只需对新加系统参数绑定即可
// 字段名,转换器,是否必须字段
cfg.addBinding(new BinderConfiguration.Binding("telephone", null, true));
}
}
- CustomCredential
package com.vim.sso;
import org.apereo.cas.authentication.UsernamePasswordCredential;
public class CustomCredential extends UsernamePasswordCredential {
private String telephone;
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
}
- CustomerAuthWebflowConfiguration
package com.vim.sso;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowExecutionPlan;
import org.apereo.cas.web.flow.CasWebflowExecutionPlanConfigurer;
import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration;
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.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.webflow.definition.registry.FlowDefinitionRegistry;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
@Configuration("customerAuthWebflowConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomerAuthWebflowConfiguration implements CasWebflowExecutionPlanConfigurer {
@Autowired
private CasConfigurationProperties casProperties;
@Autowired
@Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowDefinitionRegistry;
@Autowired
private ApplicationContext applicationContext;
@Autowired
private FlowBuilderServices flowBuilderServices;
@Bean
public CasWebflowConfigurer customWebflowConfigurer() {
// 实例化自定义的表单配置类
final CustomWebflowConfigurer c = new CustomWebflowConfigurer(flowBuilderServices, loginFlowDefinitionRegistry,
applicationContext, casProperties);
// 初始化
c.initialize();
// 返回对象
return c;
}
@Override
public void configureWebflowExecutionPlan(final CasWebflowExecutionPlan plan) {
plan.registerWebflowConfigurer(customWebflowConfigurer());
}
}
- spring.factories 文件修改
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.vim.sso.CustomAuthenticationConfiguration,com.vim.sso.CustomerAuthWebflowConfiguration
9、springboot 配置
- pom依赖
<dependency>
<groupId>org.jasig.cas.client</groupId>
<artifactId>cas-client-core</artifactId>
<version>3.5.0</version>
</dependency>
- application.properties
customCas.server-url-prefix=http://www.testserver.com:8080/cas
customCas.server-login-url=http://www.testserver.com:8080/cas/login
customCas.client-host-url=http://localhost:8081
customCas.client-ignore-url=/a/*,/b/*
- url 匹配策略 SimpleUrlPatternMatcherStrategy
package com.vim.common.config;
import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
public class SimpleUrlPatternMatcherStrategy implements UrlPatternMatcherStrategy {
private List<Pattern> patternList = new ArrayList<>();
/**
* description:判断是否匹配这个字符串
*
* @param: [url]用户请求的连接
* @return: true:不需要拦截
* false:必须要登录
*/
@Override
public boolean matches(String url) {
//使用正则表达式来匹配需要忽略的连接
boolean flag = false;
for(Pattern pattern:patternList){
if(pattern.matcher(url).find()){
flag = true;
break;
}
}
return flag;
}
/**
* description:正则表达式的规则,该规则在配置AuthenticationFilter的ignorePattern中设置
*
* @param: [pattern]
* @return: void
*/
@Override
public void setPattern(String pattern) {
String[] patternArr = pattern.split(",");
for(String s:patternArr){
patternList.add(Pattern.compile(s.trim()));
}
}
}
- casConfig 配置
package com.vim.common.config;
import org.jasig.cas.client.authentication.AuthenticationFilter;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
import org.jasig.cas.client.validation.Cas30ProxyReceivingTicketValidationFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class CasConfig {
@Value("${customCas.server-url-prefix}")
private String CAS_SERVER_URL_PREFIX;
@Value("${customCas.server-login-url}")
private String CAS_SERVER_URL_LOGIN;
@Value("${customCas.client-host-url}")
private String CAS_CLIENT_URL_HOST;
@Value("${customCas.client-ignore-url}")
private String CAS_CLIENT_URL_IGNORE;
/**
* description: 登录过滤器
* @param: []
* @return: org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean filterSingleRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new SingleSignOutFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<>();
initParameters.put("casServerUrlPrefix", CAS_SERVER_URL_PREFIX);
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(1);
return registration;
}
/**
* description:过滤验证器
* * @param: []
* @return: org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean filterValidationRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new Cas30ProxyReceivingTicketValidationFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<>();
initParameters.put("casServerUrlPrefix", CAS_SERVER_URL_PREFIX);
initParameters.put("serverName", CAS_CLIENT_URL_HOST);
initParameters.put("useSession", "true");
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(1);
return registration;
}
/**
* description:授权过滤器
* @param: []
* @return: org.springframework.boot.web.servlet.FilterRegistrationBean
*/
@Bean
public FilterRegistrationBean filterAuthenticationRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new AuthenticationFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
Map<String,String> initParameters = new HashMap<>();
initParameters.put("casServerLoginUrl", CAS_SERVER_URL_LOGIN);
initParameters.put("serverName", CAS_CLIENT_URL_HOST);
//忽略/logout的路径
initParameters.put("ignorePattern", CAS_CLIENT_URL_IGNORE);
initParameters.put("ignoreUrlPatternType", SimpleUrlPatternMatcherStrategy.class.getName());
registration.setInitParameters(initParameters);
// 设定加载的顺序
registration.setOrder(1);
return registration;
}
/**
* wraper过滤器
* @return
*/
@Bean
public FilterRegistrationBean filterWrapperRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new HttpServletRequestWrapperFilter());
// 设定匹配的路径
registration.addUrlPatterns("/*");
// 设定加载的顺序
registration.setOrder(1);
return registration;
}
/**
* 添加监听器
* @return
*/
@Bean
public ServletListenerRegistrationBean<EventListener> singleSignOutListenerRegistration(){
ServletListenerRegistrationBean<EventListener> registrationBean = new ServletListenerRegistrationBean<EventListener>();
registrationBean.setListener(new SingleSignOutHttpSessionListener());
registrationBean.setOrder(1);
return registrationBean;
}
}