cas 5.2.0登陆界面添加验证码和校验功能

本文分为4个部分:
1.登录界面添加验证码
2.自定义登录对象,增加一个验证码字段
3.自定义cas的登录流程,完成自定义校验
4.返回自定错误信息

1、配置验证码

生成验证码,使用google的kaptcha,需引入jar的包如下

        <dependency>
            <groupId>com.github.pengglegroupId>
            <artifactId>kaptchaartifactId>
            <version>2.3.2version>
        dependency>
        <dependency>
            <groupId>org.apereo.casgroupId>
            <artifactId>cas-server-core-configurationartifactId>
            <version>${cas.version}version>
        dependency>

配置验证码,以下是我自己的配置,大家可根据自身需要进行修改,网上有很多kaptcha配置参数详解

package com.zee.custom.captcha.config;

import javax.servlet.ServletException;

import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.google.code.kaptcha.servlet.KaptchaServlet;

@Configuration
public class KaptchaConfig {

    @Bean
    public ServletRegistrationBean servletRegistrationBean() throws ServletException {
        ServletRegistrationBean servlet = new ServletRegistrationBean(new KaptchaServlet(),  "/images/kaptcha.jpg");//加载路径
        servlet.addInitParameter("kaptcha.border", "no"/* kborder */);// 无边框
        servlet.addInitParameter("kaptcha.session.key", "captcha");// session key
        servlet.addInitParameter("kaptcha.textproducer.font.color", "black");
        servlet.addInitParameter("kaptcha.textproducer.font.size", "25");
        servlet.addInitParameter("kaptcha.obscurificator.impl", "com.google.code.kaptcha.impl.WaterRipple");
        servlet.addInitParameter("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
        servlet.addInitParameter("kaptcha.image.width", "90");
        servlet.addInitParameter("kaptcha.image.height", "33");
        servlet.addInitParameter("kaptcha.textproducer.char.length", "4");
        servlet.addInitParameter("kaptcha.textproducer.char.space", "5");
        servlet.addInitParameter("kaptcha.background.clear.from", "247,247,247"); // 和登录框背景颜色一致
        servlet.addInitParameter("kaptcha.background.clear.to", "247,247,247");
        return servlet;
    }
}

配置spring.factories 文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zee.custom.captcha.config.KaptchaConfig,

登录界面


<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"/>
     <script type="text/javascript" th:src="@{/themes/captcha/js/jquery.min.js}" >script>
    <script type="text/javascript" th:src="@{/themes/captcha/js/code.js}" >script>
head>
<body>

<h2>默认的登录模板h2>
<div id="casLoginContent">
    <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>
            <img id="captcha_img" th:src="@{/images/kaptcha.jpg}" onclick="changeCode()" style="width: 125px;"/>
            <input type="text" th:field="*{captcha}" id="code"/>
            <span id="code_str">span>
        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>
<script type="text/javascript">
function changeCode(){
    //刷新验证码
    $("#captcha_img").attr('src','/cas/images/kaptcha.jpg?id='+Math.random());
}
script>

2、自定义登录对象

自定义一个表单对象,需要继承默认的RememberMeUsernamePasswordCredential,添加一个验证码字段

package com.zee.custom.pojo;

import org.apereo.cas.authentication.RememberMeUsernamePasswordCredential;

public class CusLoginUserInfoEntity extends RememberMeUsernamePasswordCredential {

    private static final long serialVersionUID = 1L;

    private String captcha;

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

}

由于cas使用的是webflow所以我们还需要那自定义的登录对象绑定到页面上

package com.zee.custom.mongo.webflow;

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;

import com.zee.custom.pojo.CusLoginUserInfoEntity;

public class CustomWebflowConfigurer extends AbstractCasWebflowConfigurer {
    /**
     * 校验码动作
     */
    public static final String VALIDATE_CAPTCHA_ACTION = "validateCaptchaAction";

    public CustomWebflowConfigurer(FlowBuilderServices flowBuilderServices,
            FlowDefinitionRegistry loginFlowDefinitionRegistry, ApplicationContext applicationContext,
            CasConfigurationProperties casProperties) {
        super(flowBuilderServices, loginFlowDefinitionRegistry, applicationContext, casProperties);
        // TODO Auto-generated constructor stub
    }

    // 绑定验证码信息
    protected void doInitialize() {
        final Flow loginFlow = getLoginFlow();
        // 重写绑定自定义credential
        createFlowVariable(loginFlow, CasWebflowConstants.VAR_ID_CREDENTIAL, CusLoginUserInfoEntity.class);
        // 获取登录页
        final ViewState state = (ViewState) loginFlow.getState(CasWebflowConstants.STATE_ID_VIEW_LOGIN_FORM);
        // 获取参数绑定对象
        final BinderConfiguration cfg = getViewStateBinderConfiguration(state);
        // 由于用户名以及密码已经绑定,所以只需对新加系统参数绑定即可
        // 参数1 :字段名
        // 参数2 :转换器
        // 参数3 :是否必须的字段
        cfg.addBinding(new BinderConfiguration.Binding("captcha", null, true));

    }

}

然后把自定义的CustomWebflowConfigurer注册到spring容器中

package com.zee.custom.mongo.config;

import org.apereo.cas.configuration.CasConfigurationProperties;
import org.apereo.cas.web.flow.CasWebflowConfigurer;
import org.apereo.cas.web.flow.config.CasWebflowContextConfiguration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
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;

import com.zee.custom.mongo.webflow.CustomWebflowConfigurer;

@Configuration
@EnableConfigurationProperties(CasConfigurationProperties.class)
@AutoConfigureBefore(value = CasWebflowContextConfiguration.class)
public class CustomWebFlowConfig {
    @Autowired
    private CasConfigurationProperties casProperties;

    @Autowired
    // @Qualifier("loginFlowRegistry")
    private FlowDefinitionRegistry loginFlowRegistry;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private FlowBuilderServices flowBuilderServices;

    // 注册自定义的webFlow
    @Bean
    public CasWebflowConfigurer customWebflowConfigurer() {
        // 实例化自定义的表单配置类
        final CustomWebflowConfigurer c = new CustomWebflowConfigurer(flowBuilderServices, loginFlowRegistry,
                applicationContext, casProperties);
        // 初期化
        c.initialize();
        // 返回对象
        return c;
    }
}

配置spring.factories 文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zee.custom.captcha.config.KaptchaConfig,\
com.zee.custom.mongo.config.CustomWebFlowConfig

3、自定义cas的登录校验流程

新建一个类继承AbstractPreAndPostProcessingAuthenticationHandler 这个类,重写cas是默认登录流程,自定义登录参数的校验

import static com.mongodb.client.model.Filters.eq;

import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import javax.security.auth.login.AccountLockedException;
import javax.security.auth.login.AccountNotFoundException;
import javax.servlet.http.HttpServletRequest;

import org.apereo.cas.authentication.Credential;
import org.apereo.cas.authentication.HandlerResult;
import org.apereo.cas.authentication.PreventedException;
import org.apereo.cas.authentication.UsernamePasswordCredential;
import org.apereo.cas.authentication.handler.support.AbstractPreAndPostProcessingAuthenticationHandler;
import org.apereo.cas.authentication.principal.PrincipalFactory;
import org.apereo.cas.services.ServicesManager;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.pac4j.core.exception.MultipleAccountsFoundException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.zee.custom.mongo.exception.CusCaptchaException;
import com.zee.custom.mongo.utils.CryptoUtil;import com.zee.custom.pojo.CusLoginUserInfoEntity;


public class CustomMongoAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler {
    @Autowired
    private MongoClient mongoClient;

    public CustomMongoAuthenticationHandler(String name, ServicesManager servicesManager, PrincipalFactory principalFactory,
            Integer order) {
        super(name, servicesManager, principalFactory, order);
    }

    @Override
    public boolean supports(Credential credential) {
        // TODO Auto-generated method stub
        System.out.println("被调用了");
        return credential instanceof UsernamePasswordCredential;
    }

    /*
     * 验证码,自定义验证, 验证用户名密码是否正确
     */
    protected HandlerResult doAuthentication(Credential credential)
            throws GeneralSecurityException, PreventedException {

        final CusLoginUserInfoEntity myCredential = (CusLoginUserInfoEntity) credential;
        // 通过SpringMvc封装的方法获得本次请求的request
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
        //获取存储在Session中的验证码
        String captcha = request.getSession().getAttribute("captcha").toString();
        if (!myCredential.getCaptcha().equals(captcha)) {
            //此处抛出的是自定义异常,后面说明如何自定义异常,自定义返回错误信息
            throw new CusCaptchaException("验证码错误");
        }
        System.out.println(request.getSession().getAttribute("captcha"));
        //自定义其他校验
    }

}

自定义的验证处理流程注册到spring容器中

package com.zee.custom.mongo.config;

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.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.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.zee.custom.mongo.handler.CustomMongoAuthenticationHandler;

@Configuration("CustomAuthenticationHandlerConfiguration")
@EnableConfigurationProperties(CasConfigurationProperties.class)
public class CustomAuthenticationHandlerConfiguration implements AuthenticationEventExecutionPlanConfigurer {
    @Autowired
    @Qualifier("servicesManager")
    private ServicesManager servicesManager;

    // 注册验证器
    @Bean
    public AuthenticationHandler customAuthenticationHandler() {
        return new CustomMongoAuthenticationHandler("customAuthenticationHandler", servicesManager,
                new DefaultPrincipalFactory(), 1);
    }

    // 注册自定义认证器
    public void configureAuthenticationExecutionPlan(AuthenticationEventExecutionPlan plan) {
        // TODO Auto-generated method stub
        plan.registerAuthenticationHandler(customAuthenticationHandler());
    }

}

配置spring.factories 文件,完整的spring.factories 文件

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.zee.custom.mongo.config.CustomAuthenticationHandlerConfiguration,\
com.zee.custom.captcha.config.KaptchaConfig,\
com.zee.custom.mongo.config.CustomWebFlowConfig

4、自定义返回错误信息

当输入的验证码不正确时,我们需要自定义异常和错误信息
创建异常类,需要继承AccountExpiredException这个类

package com.zee.custom.mongo.exception;

import javax.security.auth.login.AccountExpiredException;

public class CusCaptchaException extends AccountExpiredException {

    private static final long serialVersionUID = 1L;

    public CusCaptchaException() {
        super();
        // TODO Auto-generated constructor stub
    }

    public CusCaptchaException(String msg) {
        super(msg);
        // TODO Auto-generated constructor stub
    }
}

配置application.properties

#自定义异常,多个逗号隔开
cas.authn.exceptions.exceptions= com.zee.custom.mongo.exception.CusCaptchaException

把cas原来的messages_zh_CN.properties文件复制到src/mian/resources目录下面,并配置需要提示的信息

#自定义字段如果为必须输入,当为null时,提示信息格式为  字段名+required
captcha.required=必须输入验证码
#自定义异常信息格式为 authenticationFailure+异常类名
authenticationFailure.CusCaptchaException=验证码错误

最终的项目结构

cas 5.2.0登陆界面添加验证码和校验功能_第1张图片

参考文章:https://blog.csdn.net/yelllowcong/article/category/7347371

你可能感兴趣的:(cas)