CAS单点登录-自定义认证之重写Credential(十五)

来源:互联网 发布:php ajax返回json数据 编辑:程序博客网 时间:2024/06/13 01:27

CAS单点登录-自定义认证之重写Credential (十五)

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
  • 对登录页的flowCasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM重写绑定参数
  • 登录页锁定参数
  • 自定义AuthenticationHandler
  • 采用spring.factories注册

程序

UsernamePasswordSysCredential

该代码定义前端所需定义的绑定参数,后续会交给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));    }}

UsernamePasswordSystemAuthenticationHandler

当用户名为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;    }}

注册CasWebflowConfigurer

这里是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;    }}

注册AuthenticationHandler

/* * 版权所有.(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());    }}

spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.carl.sso.support.auth.config.CustomerAuthWebflowConfiguration,com.carl.sso.support.auth.config.CustomAuthenticationEventExecutionPlanConfiguration

casLoginView.html

新加绑定系统信息

<!DOCTYPE html><!--  ~ 版权所有.(c)2008-2017.卡尔科技工作室  --><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">SSO</option>                    <option value="oa">OA</option>                    <option value="crm">CRM</option>                </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">SSO</option>                    <option value="oa">OA</option>                    <option value="crm">CRM</option>                </select>            </div>        </section>

测试

  1. 由于配置了主题,demo主题在触发到demo主题下才会进入以上节目
  2. 由于继承了RememberMeUsernamePasswordCredential所以其他配置该credential依然生效
  3. 由于自定义UsernamePasswordSystemAuthenticationHandler的认证循序放在第一,所以优先验证

这里写图片描述

这里写图片描述

这里写图片描述

注意事项

  1. 优先绑定Credential @AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
  2. 页面绑定新参数th:field="*{system}"
  3. 采用spring.factories注册
  4. flow绑定新参数CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM

下载代码尝试:GitHub 其他版本可以到GitHub或者码云查看

发现一些意外的事情可以考虑翻翻前面的博客进行学习哦

作者联系方式

如果技术的交流或者疑问可以联系或者提出issue。

邮箱:huang.wenbin@foxmail.com

QQ: 756884434 (请注明:SSO-CSDN)

阅读全文
1 0
原创粉丝点击