ps:这一章拖了非常久,最近事情也比较多,希望能谅解~
再次目前使用cas版本为:5.1.5
我们使用sso的时候往往登录不只是需要用户名密码,更多的是有时候选择部门,系统等等的扩展校验信息,当然有时候还有校验码策略,例如同一个IP10分钟内输入密码错误3次输入验证码等等的业务场景~
那么这一章讲一下如何扩展校验信息以及自定义校验器。
业务场景:
1. 登录页新增系统下拉框选择,当是SSO时,匹配自定义校验器
2. 自定义校验器,当系统未sso并且用户名为admin时,允许认证成功
本章目的,自定义Credential以及自定义
AuthenticationHandler
所需知识:
1. 对cas有所了解
2. 对自定义AuthenticationHandler有所了解
3. 对cas的credential
有所了解
4. 对spring webflow有基础了解
5. spring boot的使用经验
若不具备以上知识或许看以下的内容有所吃力。
流程:
CasWebflowConstants.VAR_ID_CREDENTIAL
CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM
重写绑定参数AuthenticationHandler
spring.factories
注册该代码定义前端所需定义的绑定参数,后续会交给AuthenticationHandler进行认证
为了测试简单,也不影响其他鉴权模式,所以直接继承RememberMeUsernamePasswordCredential,再日常的开发慎重考虑这个bean的设计
/*
* 版权所有.(c)2008-2017. 卡尔科技工作室
*/
package com.carl.sso.support.auth;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apereo.cas.authentication.RememberMeUsernamePasswordCredential;
import javax.validation.constraints.Size;
/**
* 用户名,密码,系统
*
* @author Carl
* @date 2017/10/23
* @since
*/
public class UsernamePasswordSysCredential extends RememberMeUsernamePasswordCredential {
@Size(min = 2, message = "require system")
private String system;
public String getSystem() {
return system;
}
public UsernamePasswordSysCredential setSystem(String system) {
this.system = system;
return this;
}
@Override
public int hashCode() {
return new HashCodeBuilder()
.appendSuper(super.hashCode())
.append(this.system)
.toHashCode();
}
}
前端重写绑定参数,并且重写指定原有的“
/*
* 版权所有.(c)2008-2017. 卡尔科技工作室
*/
package com.carl.sso.support.auth;
import org.apereo.cas.web.flow.AbstractCasWebflowConfigurer;
import org.apereo.cas.web.flow.CasWebflowConstants;
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;
/**
* 重新定义默认的web流程
*
* @author Carl
* @date 2017/10/23
* @since 1.6.0
*/
public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices, FlowDefinitionRegistry flowDefinitionRegistry) {
super(flowBuilderServices, flowDefinitionRegistry);
}
@Override
protected void doInitialize() throws Exception {
final Flow flow = getLoginFlow();
bindCredential(flow);
}
/**
* 绑定输入信息
*
* @param flow
*/
protected void bindCredential(Flow flow) {
//重写绑定自定义credential
createFlowVariable(flow, CasWebflowConstants.VAR_ID_CREDENTIAL, UsernamePasswordSysCredential.class);
//登录页绑定新参数
final ViewState state = (ViewState) flow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
//由于用户名以及密码已经绑定,所以只需对新加系统参数绑定即可
cfg.addBinding(new BinderConfiguration.Binding("system", null, false));
}
}
当用户名为admin,并且system为sso即允许通过为了测试简单才定义简单的逻辑,开发过程中慎重考虑
/*
* 版权所有.(c)2008-2017. 卡尔科技工作室
*/
package com.carl.sso.support.auth.handler;
import com.carl.sso.support.auth.UsernamePasswordSysCredential;
import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.HandlerResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import javax.security.auth.login.AccountNotFoundException;
import java.security.GeneralSecurityException;
import java.util.Collections;
/**
* 用户名系统认证,只要是admin用户加上sso系统就允许通过
*
* @author Carl
* @date 2017/10/23
* @since 1.6.0
*/
public class UsernamePasswordSystemAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
public UsernamePasswordSystemAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory, Integer order) {
super(name, servicesManager, principalFactory, order);
}
@Override
protected HandlerResult doAuthentication(Credential credential) throws GeneralSecurityException, PreventedException {
//当用户名为admin,并且system为sso即允许通过
UsernamePasswordSysCredential sysCredential = (UsernamePasswordSysCredential) credential;
if ("admin".equals(sysCredential.getUsername()) && "sso".equals(sysCredential.getSystem())) {
//这里可以自定义属性数据
return createHandlerResult(credential, this.principalFactory.createPrincipal(((UsernamePasswordSysCredential) credential).getUsername(), Collections.emptyMap()), null);
} else {
throw new AccountNotFoundException("必须是admin用户才允许通过");
}
}
@Override
public boolean supports(Credential credential) {
return credential instanceof UsernamePasswordSysCredential;
}
}
这里是spring boot的知识,需要对配置进行识别
由于需要对Credential进行重写定义,必须在该配置之前注册,否则自定义的无法重写
@AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
/*
* 版权所有.(c)2008-2017. 卡尔科技工作室
*/
package com.carl.sso.support.auth.config;
import com.carl.sso.support.auth.CustomWebflowConfigurer;
import org.apereo.cas.config.CasWebflowContextConfiguration;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
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;
/**
* @author Carl
* @date 2017/10/23
* @since 1.6.0
*/
@Configuration("customerAuthWebflowConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
@AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
public class CustomerAuthWebflowConfiguration {
@Autowired
@Qualifier("logoutFlowRegistry")
private FlowDefinitionRegistry logoutFlowRegistry;
@Autowired
@Qualifier("loginFlowRegistry")
private FlowDefinitionRegistry loginFlowRegistry;
@Autowired
@Qualifier("builder")
private FlowBuilderServices builder;
@Bean
public CasWebflowConfigurer customWebflowConfigurer() {
final CustomWebflowConfigurer c = new CustomWebflowConfigurer(builder, loginFlowRegistry);
c.setLogoutFlowDefinitionRegistry(logoutFlowRegistry);
return c;
}
}
/*
* 版权所有.(c)2008-2017. 卡尔科技工作室
*/
package com.carl.sso.support.auth.config;
import com.carl.sso.support.auth.handler.UsernamePasswordSystemAuthenticationHandler;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlan;
import org.apereo.cas.authentication.AuthenticationEventExecutionPlanConfigurer;
import org.apereo.cas.authentication.AuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.services.ServicesManager;
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;
/**
* @author Carl
* @date 2017/10/23
* @since 1.6.0
*/
@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, jdbcPrincipalFactory, 1);
}
//注册自定义认证器
@Override
public void configureAuthenticationExecutionPlan(final AuthenticationEventExecutionPlan plan) {
plan.registerAuthenticationHandler(customAuthenticationHandler());
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.carl.sso.support.auth.config.CustomerAuthWebflowConfiguration,com.carl.sso.support.auth.config.CustomAuthenticationEventExecutionPlanConfiguration
新加绑定系统信息
<html>
<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="${#themes.code('demo.pageTitle')}">title>
<link rel="stylesheet" th:href="@{${#themes.code('demo.css.file')}}"/>
head>
<body>
<h1 th:text="${#themes.code('demo.pageTitle')}">h1>
<div>
<form method="post" th:object="${credential}">
<div th:if="${#fields.hasErrors('*')}">
<span th:each="err : ${#fields.errors('*')}" th:utext="${err}"/>
div>
<h2 th:utext="#{screen.welcome.instructions}">h2>
<section class="row">
<label for="username" th:utext="#{screen.welcome.label.netid}"/>
<div th:unless="${openIdLocalId}">
<input class="required"
id="username"
size="25"
tabindex="1"
type="text"
th:disabled="${guaEnabled}"
th:field="*{username}"
th:accesskey="#{screen.welcome.label.netid.accesskey}"
autocomplete="off"/>
div>
section>
<section class="row">
<label for="password" th:utext="#{screen.welcome.label.password}"/>
<div>
<input class="required"
type="password"
id="password"
size="25"
tabindex="2"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{password}"
autocomplete="off"/>
div>
section>
<section class="row">
<label for="system" >系统label>
<div>
<select class="required"
id="system"
tabindex="2"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{system}"
autocomplete="off">
<option value="sso">SSOoption>
<option value="oa">OAoption>
<option value="crm">CRMoption>
select>
div>
section>
<section>
<input type="hidden" name="execution" th:value="${flowExecutionKey}"/>
<input type="hidden" name="_eventId" value="submit"/>
<input type="hidden" name="geolocation"/>
<input class="btn btn-submit btn-block"
name="submit"
accesskey="l"
th:value="#{screen.welcome.button.login}"
tabindex="6"
type="submit"/>
section>
form>
div>
body>
html>
重点为以下代码
<section class="row">
<label for="system" >系统label>
<div>
<select class="required"
id="system"
tabindex="2"
th:accesskey="#{screen.welcome.label.password.accesskey}"
th:field="*{system}"
autocomplete="off">
<option value="sso">SSOoption>
<option value="oa">OAoption>
<option value="crm">CRMoption>
select>
div>
section>
UsernamePasswordSystemAuthenticationHandler
的认证循序放在第一,所以优先验证Credential
@AutoConfigureBefore(value = CasWebflowContextConfiguration.class)th:field="*{system}"
CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM
下载代码尝试: 其他版本可以到GitHub或者码云查看
发现一些意外的事情可以考虑翻翻前面的博客进行学习哦
如果技术的交流或者疑问可以联系或者提出issue。
QQ: 756884434 (请注明:SSO-CSDN)