龙岗附近做网站公司,中关村在线官网首页,广告设计图片用什么软件,石家庄网站建设需要多少钱介绍
CAS#xff08;Central Authentication Service#xff09;中心授权/认证服务#xff0c;是由耶鲁大学发起的一个开源项目#xff0c;距今已有20年之久#xff0c;功能相当丰富#xff0c;目的在于为Web应用系统提供一种可靠且稳定的单点登录解决方案。
CAS分为…介绍
CASCentral Authentication Service中心授权/认证服务是由耶鲁大学发起的一个开源项目距今已有20年之久功能相当丰富目的在于为Web应用系统提供一种可靠且稳定的单点登录解决方案。
CAS分为两个模块一个是CAS Server认证服务器主要用于票据颁发以及进行票据校验CAS Client为客户端当有服务组件需接入CAS时引入客户端并做一些简单的配置就可以接入了CAS。
前言
本文主要阐述CAS Server服务端Maven构建以及自定义扩展使用。对于CAS Server的执行流程以及原理本文就不详细概述了。
在实际开发情况中我们需要对CAS Server做一些自定义扩展便于解决一些实际问题官方提供了多种使用以及扩展方式例如使用cas-overlay-template或者cas-initializr进行第三方扩展但是都是使用Gradle进行集成以及使用Tomcat容器单独部署在一些特定的开发场景下会比较麻烦。
项目构建
构建各个版本如下 CAS Server6.5.9 JDK11 Spring Boot2.6.15
由于CAS官方6.5.9版本默认使用的是SpringBoot 2.6.x和JDK11版本大多数模块包根据这个版本进行的开发如果降低版本的话可能会存在一些不兼容问题。
Maven依赖
如下 propertiesmaven.compiler.source11/maven.compiler.sourcemaven.compiler.target11/maven.compiler.targetjdk.version11/jdk.versioncas.version6.5.9/cas.versionspring-boot.version2.6.15/spring-boot.version/properties
dependencyManagementdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-dependencies/artifactIdversion${spring-boot.version}/versionscopeimport/scopetypepom/type/dependency/dependencies/dependencyManagement
dependencies!-- CAS server dependencis start --dependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-webapp-init/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-webapp-config/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-webapp-init-tomcat/artifactIdversion${cas.version}/version/dependency
dependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core/artifactIdversion${cas.version}/version/dependency
dependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-cookie/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-services/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-web/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-util/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-tickets/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-audit/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-logout/artifactIdversion${cas.version}/version/dependency!-- 前端包样式 --dependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-thymeleaf/artifactIdversion${cas.version}/versionexclusionsexclusionartifactIdmaterial-components-web/artifactIdgroupIdorg.webjars.npm/groupId/exclusionexclusionartifactIdcss-vars-ponyfill/artifactIdgroupIdorg.webjars.npm/groupId/exclusion/exclusions/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-pm/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-json-service-registry/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-person-directory/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-person-directory-core/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-support-validation/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-web-api/artifactIdversion${cas.version}/version/dependencydependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-authentication-api/artifactIdversion${cas.version}/version/dependency!-- CAS server dependencis end --dependencygroupIdorg.springframework.security/groupIdartifactIdspring-security-config/artifactId/dependency
!-- 用于自定义WebFlow流程 --dependencygroupIdorg.apereo.cas/groupIdartifactIdcas-server-core-webflow-api/artifactIdversion${cas.version}/version/dependency!-- 控制cas server日志打印管理默认使用自带的log4j.xml管理日志 --dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-logging/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdversion1.18.4/version/dependency/dependencies
注其实大多数依赖包存在被其他依赖引用的但是依赖类型为runtime会导致在自定义扩展的是时候找不到对应的扩展类。
可以使用Spring Boot Maven Plugin插件进行构建打包或者使用Maven自身打包插件也是可以的。
配置文件
spring.main.allow-bean-definition-overridingtrue
# 使用默认的账号名称使用默认校验方式
cas.authn.accept.enabledtrue
cas.authn.accept.userscasuser::mellon
cas.authn.accept.nameStatic Credentials
cas.tgc.securefalse
# 客户端注册校验
cas.serviceRegistry.initFromJsontrue
cas.serviceRegistry.json.watcherEnabledtrue
# 指定services注解json文件地址
cas.serviceRegistry.json.locationclasspath:/services
# 嵌入式Tomcat配置
server.port8443
server.servlet.context-path/cas
配置服务认证注册文件
在resources文件夹下新建services/${name}-${id}.json文件name与id为内容的值内容如下
{class: org.apereo.cas.services.RegexRegisteredService,serviceId: ^(https|imaps|http)://.*,name: HTTPandIMAPS,id: 10000001,description: This service definition authorizes all application urls that support HTTP and IMAPS protocols.,evaluationOrder: 10000
}
该文件主要是针对其他服务注册到CAS Server中的校验。
项目启动
找到cas-server-webapp-init依赖包中的CasWebApplication类直接启动即可。 自定义扩展
目前就阐述一下在开发过程中可能存在的一些扩展功能。在CAS Server中无法使用Configuration以及类扫描方式进行注入只能通过spring.factories文件注册的方式进行注入即Spring Boot自定配置方式注入。
自定义登陆校验方式
目前CAS本身也支持很多三方校验扩展比如MySQL数据库校验、Ldap校验等等但是那些都存在一定的限制性。
先创建一个自定义校验处理器ExampleAuthenticationHandler该类继承AbstractUsernamePasswordAuthenticationHandler实现对应方法即可主要校验逻辑在authenticateUsernamePasswordInternal方法中自定义内容如下。 Overrideprotected AuthenticationHandlerExecutionResult authenticateUsernamePasswordInternal(UsernamePasswordCredential credential, String originalPassword) throws GeneralSecurityException, PreventedException {LOGGER.info(The is example authentication...);// 登陆校验// ....// 返回成果结果AuthenticationHandlerExecutionResult result new DefaultAuthenticationHandlerExecutionResult((AuthenticationHandler) credential,new BasicCredentialMetaData((Credential) this.principalFactory.createPrincipal(credential.getUsername())));return result;}
在这个类中可以使用任意方式的进行校验账号密码是否可正常登录如果校验失败则直接报错即可这里可以自定义一些登陆报错异常类。
需将该校验类注入到CAS本身的校验计划中新建一个计划配置类实现AuthenticationEventExecutionPlanConfigurer接口内容如下
public class ExampleAuthenticationEventExecutionPlanConfiguration implements AuthenticationEventExecutionPlanConfigurer {
Autowiredprivate ServicesManager servicesManager;
Overridepublic void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) throws Exception {ExampleAuthenticationHandler myAuthenticationHandler new ExampleAuthenticationHandler(ExampleAuthenticationHandler.class.getName(),servicesManager, new DefaultPrincipalFactory(), 1);plan.registerAuthenticationHandler(myAuthenticationHandler);}
}
需在resources下新建META-INF/spring.factories并写入如下内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration\com.example.config.ExampleAuthenticationEventExecutionPlanConfiguration
自定义API接口
在一些特定的场景下需要CAS Server提供一些API接口由于CAS Server使用Spring Security进行接口权限控制如果使用Spring Security的方式进行放开的话即继承WebSecurityConfigurerAdapter类将放开的路径写入会发现毫无作用只能通过CAS Server提供接口来实现接口的放开。
新建的接口类型需手动配置在配置类中。
新建类实现ProtocolEndpointWebSecurityConfigurer接口将需要放开的API接口写入
public class SecurityConfig implements ProtocolEndpointWebSecurityConfigurer {Overridepublic ListString getIgnoredEndpoints() {ListString ignoreList new ArrayList();ignoreList.add(/web/);return ignoreList;}
}
将该类写入到spring.factories文件中。
org.springframework.boot.autoconfigure.EnableAutoConfiguration\com.example.config.ExampleAuthenticationEventExecutionPlanConfiguration,\com.example.config.SecurityConfig
在服务启动的日志中就能看到/web/开头的接口已被Spring Security放开 自定义登录参数
当前都是使用username以及password参数进行校验的在一些业务场景下登录界面需要会存在需校验验证码、IP地址等一些信息这就涉及到前端页面登陆改动以及后端需进行参数绑定由于CAS Server是通过Spring WebFlow进行页面参数的绑定。
前端自定义界面更改
覆盖页面
如果是界面改动较大的话可以直接覆盖CAS本身的前端页面前端页面文件都在cas-server-support-thymeleaf依赖中 可以将其完全覆盖如果不愿意覆盖并想修改登陆页面时可以将登陆页面进行修改新建一个登录页在login/casLoginView1.html新建一个类继承DefaultLoginWebflowConfigurer类用于指定自定义页面
public class ExampleDefaultLoginWebflowConfigurer extends DefaultLoginWebflowConfigurer {
public ExampleDefaultLoginWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry flowDefinitionRegistry, ConfigurableApplicationContext applicationContext, CasConfigurationProperties casProperties) {super(flowBuilderServices, flowDefinitionRegistry, applicationContext, casProperties);}
Overrideprotected void createLoginFormView(Flow flow) {val propertiesToBind CollectionUtils.wrapList(username, password, source);val binder createStateBinderConfiguration(propertiesToBind);
casProperties.getView().getCustomLoginFormFields().forEach((field, props) - {val fieldName String.format(customFields[%s], field);binder.addBinding(new BinderConfiguration.Binding(fieldName, props.getConverter(), props.isRequired()));});// 指定登录页面val state createViewState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, login/casLoginView1, binder);
state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));createStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordCredential.class);
val transition createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, CasWebflowConstants.STATE_ID_REAL_SUBMIT);val attributes transition.getAttributes();attributes.put(bind, Boolean.TRUE);attributes.put(validate, Boolean.TRUE);attributes.put(history, History.INVALIDATE);}在配置类中将该配置注入到容器中用于覆盖CAS原来注入的类所以Bean的名称一定修改为defaultWebflowConfigurer。 Bean(name defaultWebflowConfigurer)Order(Ordered.HIGHEST_PRECEDENCE)public CasWebflowConfigurer defaultWebflowConfigurer(final ConfigurableApplicationContext applicationContext,final CasConfigurationProperties casProperties,Qualifier(CasWebflowConstants.BEAN_NAME_LOGIN_FLOW_DEFINITION_REGISTRY)final FlowDefinitionRegistry loginFlowRegistry,Qualifier(CasWebflowConstants.BEAN_NAME_LOGOUT_FLOW_DEFINITION_REGISTRY)final FlowDefinitionRegistry logoutFlowRegistry,Qualifier(CasWebflowConstants.BEAN_NAME_FLOW_BUILDER_SERVICES)final FlowBuilderServices flowBuilderServices) {val c new ExampleDefaultLoginWebflowConfigurer(flowBuilderServices, loginFlowRegistry, applicationContext, casProperties);c.setLogoutFlowDefinitionRegistry(logoutFlowRegistry);c.setOrder(Ordered.HIGHEST_PRECEDENCE);return c;}
通过CAS本身扩展原有页面
如果前端仅仅是加一个字段或者表格可以通过CAS本身的一个配置进行扩展
在后台配置文件中添加如下配置
cas.view.custom-login-form-fields.address.messageBundleKeyaddress
cas.view.custom-login-form-fields.address.requiredfalse
配置含义在登陆界面为扩展一个address字段属性为非必填但是前端输入框默认为文本框。 后端绑定自定义属性
如果前端采用扩展原有的登录页面的时候那么提交过来的数据在后端会自定绑定在UsernamePasswordCredential.customFields中在自定义校验处理类中可以获取到customFields的数据。
如果前端是通过覆盖的形式新增登录表单参数时后端需重写UsernamePasswordCredential用于新的参数绑定
新建一个类继承UsernamePasswordCredential并且新增自定义参数
public class ExampleUsernamePasswordCredential extends UsernamePasswordCredential {
private String address;
public String getAddress() {return address;}
public void setAddress(String address) {this.address address;}
public ExampleUsernamePasswordCredential(String username, String password, String address) {super(username, password);this.address address;}
Overridepublic void validate(ValidationContext context) {super.validate(context);}
}
在之前的那个指定登录页的ExampleDefaultLoginWebflowConfigurer配置类中需将address参数绑定到页面上并且更改绑定的Credential Overrideprotected void createLoginFormView(Flow flow) {val propertiesToBind CollectionUtils.wrapList(username, password, source);val binder createStateBinderConfiguration(propertiesToBind);
casProperties.getView().getCustomLoginFormFields().forEach((field, props) - {val fieldName String.format(customFields[%s], field);binder.addBinding(new BinderConfiguration.Binding(fieldName, props.getConverter(), props.isRequired()));});// 绑定address参数binder.addBinding(new BinderConfiguration.Binding(address, null, false));val state createViewState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, login/casLoginView, binder);
state.getRenderActionList().add(createEvaluateAction(CasWebflowConstants.ACTION_ID_RENDER_LOGIN_FORM));// 更改为自定义CredentialcreateStateModelBinding(state, CasWebflowConstants.VAR_ID_CREDENTIAL, ExampleUsernamePasswordCredential.class);
val transition createTransitionForState(state, CasWebflowConstants.TRANSITION_ID_SUBMIT, CasWebflowConstants.STATE_ID_REAL_SUBMIT);val attributes transition.getAttributes();attributes.put(bind, Boolean.TRUE);attributes.put(validate, Boolean.TRUE);attributes.put(history, History.INVALIDATE);}// 用于更改createFlowVariableOverrideprotected void createRememberMeAuthnWebflowConfig(Flow flow) {if (casProperties.getTicket().getTgt().getRememberMe().isEnabled()) {createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, RememberMeUsernamePasswordCredential.class);val state getState(flow, CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM, ViewState.class);val cfg getViewStateBinderConfiguration(state);cfg.addBinding(new BinderConfiguration.Binding(rememberMe, null, false));} else {// 更改为自定义CredentialcreateFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, ExampleUsernamePasswordCredential.class);}}
后续还会继续分享CAS相关的东西大家可以一起讨论