<?xml version="1.0" encoding="UTF-8"?>
<flow xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns="http://www.springframework.org/schema/webflow"
      xsi:schemaLocation="http://www.springframework.org/schema/webflow
                          http://www.springframework.org/schema/webflow/spring-webflow.xsd">

    <action-state id="initialAuthenticationRequestValidationCheck">
        <evaluate expression="initialAuthenticationRequestValidationAction"/>
        <transition on="authenticationFailure" to="handleAuthenticationFailure"/>
        <transition on="error" to="initializeLoginForm"/>
        <transition on="success" to="ticketGrantingTicketCheck"/>
        <transition on="successWithWarnings" to="ticketGrantingTicketCheck"/>
    </action-state>

    <action-state id="ticketGrantingTicketCheck">
        <evaluate expression="ticketGrantingTicketCheckAction"/>
        <transition on="notExists" to="gatewayRequestCheck"/>
        <transition on="invalid" to="terminateSession"/>
        <transition on="valid" to="hasServiceCheck"/>
        <!-- ticket检查:权限校验出现异常-->
        <transition on="systemAuthError" to="initializeLoginForm"/>
        <!-- ticket检查:权限校验不通过,不允许访问该系统-->
        <transition on="systemAuthInvalid" to="systemAuthManageCheck"/>
    </action-state>

    <view-state id="systemAuthManageCheck" view="systemAuthInvalid"/>
    <!--    <view-state id="systemAuthError" view="systemAuthError"/>-->

    <action-state id="initializeLoginForm">
        <evaluate expression="initializeLoginAction"/>
        <transition on="success" to="viewLoginForm"/>
    </action-state>

    <view-state id="viewLoginForm" view="casLoginView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <!-- 获取页面配置信息 -->
        <on-entry>
            <!-- viewScope 只能在视图状态中访问,而 flowScope 可以在整个流程中访问。因此,你可以将 outputViewConfig 存储在 flowScope 中,然后在决策状态中访问它。 -->
            <evaluate expression="viewConfigServiceImpl.getConfig(flowRequestContext)" result="flowScope.outputViewConfig"/>
        </on-entry>
        <!-- invalidate:失能所有回退view,即当前及之前所有view都是不能够被回退的 -->
        <!-- discard:失能一个回退view,即当前view不能再下一个view上回退,回退到的是前一个view -->
        <transition on="submit" bind="true" validate="true" to="determineTargetPage" history="invalidate"/>
    </view-state>

    <!-- 决策状态,判断页面来源,desktop的跳转专用页面,其他的,跳转通用页面 -->
    <decision-state id="determineTargetPage">
        <if test="flowScope.outputViewConfig.pageType == 'desktop'" then="desktopCaptcha" else="captcha"/>
    </decision-state>

    <!-- desktop主题专用,校验是否需要验证码 com.demo.action.CaptchaAction-->
    <action-state id="desktopCaptcha">
        <evaluate expression="captchaAction"/>
        <!-- 密码错误,重新回到登录页面 -->
        <transition on="error" to="initializeLoginForm"/>
        <!-- 密码正确,但是密码临期或已过期 -->
        <transition on="needPwdWarn" to="desktopPwdWarnForm"/>
        <!-- 密码正确,但是需要验证码 -->
        <transition on="needCaptcha" to="desktopCaptchaForm"/>
        <!-- 密码正确,并且不需要验证码 -->
        <transition on="success" to="desktopRealSubmit"/>
    </action-state>

    <!-- 通用,校验是否需要验证码 com.demo.action.CaptchaAction-->
    <action-state id="captcha">
        <evaluate expression="captchaAction"/>
        <!-- 密码错误,重新回到登录页面 -->
        <transition on="error" to="initializeLoginForm"/>
        <!-- 密码正确,但是密码临期或已过期 -->
        <transition on="needPwdWarn" to="pwdWarnForm"/>
        <!-- 密码正确,但是需要验证码 -->
        <transition on="needCaptcha" to="captchaForm"/>
        <!-- 密码正确,并且不需要验证码 -->
        <transition on="success" to="realSubmit"/>
    </action-state>

    <!-- desktop主题专用,密码临期或已过期页面 -->
    <view-state id="desktopPwdWarnForm" view="desktopPwdWarnView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <!-- 获取页面配置信息 -->
        <on-entry>
            <evaluate expression="viewConfigServiceImpl.getConfig(flowRequestContext)" result="viewScope.outputViewConfig"/>
        </on-entry>
        <!-- invalidate:失能所有回退view,即当前及之前所有view都是不能够被回退的 -->
        <!-- discard:失能一个回退view,即当前view不能再下一个view上回退,回退到的是前一个view -->
        <transition on="submit" bind="true" validate="true" to="desktopPwdWarn" history="invalidate"/>
    </view-state>

    <!-- 通用,密码临期或已过期页面 -->
    <view-state id="pwdWarnForm" view="pwdWarnView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <!-- 获取页面配置信息 -->
        <on-entry>
            <evaluate expression="viewConfigServiceImpl.getConfig(flowRequestContext)"
                      result="viewScope.outputViewConfig"/>
        </on-entry>
        <!-- invalidate:失能所有回退view,即当前及之前所有view都是不能够被回退的 -->
        <!-- discard:失能一个回退view,即当前view不能再下一个view上回退,回退到的是前一个view -->
        <transition on="submit" bind="true" validate="true" to="pwdWarn" history="invalidate"/>
    </view-state>

    <!-- desktop主题专用,校验是否需要验证码 com.demo.action.PwdWarnAction-->
    <action-state id="desktopPwdWarn">
        <evaluate expression="pwdWarnAction"/>
        <!-- 错误或已过期 -->
        <transition on="error" to="initializeLoginForm"/>
        <!-- 需要验证码 -->
        <transition on="needCaptcha" to="desktopCaptchaForm"/>
        <!-- 不需要验证码 -->
        <transition on="success" to="desktopRealSubmit"/>
    </action-state>

    <!-- 通用,校验是否需要验证码 com.demo.action.PwdWarnAction-->
    <action-state id="pwdWarn">
        <evaluate expression="pwdWarnAction"/>
        <!-- 错误或已过期 -->
        <transition on="error" to="initializeLoginForm"/>
        <!-- 需要验证码 -->
        <transition on="needCaptcha" to="captchaForm"/>
        <!-- 不需要验证码 -->
        <transition on="success" to="realSubmit"/>
    </action-state>

    <!-- desktop主题专用,验证码页面 -->
    <view-state id="desktopCaptchaForm" view="desktopCaptchaView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <!-- 获取页面配置信息 -->
        <on-entry>
            <evaluate expression="viewConfigServiceImpl.getConfig(flowRequestContext)" result="viewScope.outputViewConfig"/>
        </on-entry>
        <!-- invalidate:失能所有回退view,即当前及之前所有view都是不能够被回退的 -->
        <!-- discard:失能一个回退view,即当前view不能再下一个view上回退,回退到的是前一个view -->
        <transition on="submit" bind="true" validate="true" to="desktopRealSubmit" history="invalidate"/>
    </view-state>

    <!-- 通用,验证码页面 -->
    <view-state id="captchaForm" view="captchaView" model="credential">
        <binder>
            <binding property="username" required="true"/>
            <binding property="password" required="true"/>
        </binder>
        <!-- 获取页面配置信息 -->
        <on-entry>
            <evaluate expression="viewConfigServiceImpl.getConfig(flowRequestContext)"
                      result="viewScope.outputViewConfig"/>
        </on-entry>
        <!-- invalidate:失能所有回退view,即当前及之前所有view都是不能够被回退的 -->
        <!-- discard:失能一个回退view,即当前view不能再下一个view上回退,回退到的是前一个view -->
        <transition on="submit" bind="true" validate="true" to="realSubmit" history="invalidate"/>
    </view-state>

    <!-- desktop主题专用,真正提交表单 -->
    <action-state id="desktopRealSubmit">
        <evaluate expression="authenticationViaFormAction"/>
        <transition on="warn" to="warn"/>
        <transition on="success" to="loginState"/>
        <transition on="successWithWarnings" to="showAuthenticationWarningMessages"/>
        <!-- https://blog.csdn.net/cl_andywin/article/details/53380117 -->
        <!-- <transition on="authenticationFailure" to="handleAuthenticationFailure"/> -->
        <transition on="authenticationFailure" to="desktopErrorMsg"/>
        <transition on="error" to="showAuthenticationWarningMessages"/>
    </action-state>

    <!-- 通用,真正提交表单 -->
    <action-state id="realSubmit">
        <evaluate expression="authenticationViaFormAction"/>
        <transition on="warn" to="warn"/>
        <transition on="success" to="loginState"/>
        <transition on="successWithWarnings" to="showAuthenticationWarningMessages"/>
        <!-- https://blog.csdn.net/cl_andywin/article/details/53380117 -->
        <!-- <transition on="authenticationFailure" to="handleAuthenticationFailure"/> -->
        <transition on="authenticationFailure" to="errorMsg"/>
        <transition on="error" to="showAuthenticationWarningMessages"/>
    </action-state>

    <!-- desktop主题专用,错误提示信息 com.demo.action.CaptchaErrorAction -->
    <action-state id="desktopErrorMsg">
        <evaluate expression="errorMsgAction"/>
        <transition on="success" to="desktopCaptchaForm"/>
        <transition on="error" to="initializeLoginForm"/>
    </action-state>

    <!-- 通用,错误提示信息 com.demo.action.CaptchaErrorAction -->
    <action-state id="errorMsg">
        <evaluate expression="errorMsgAction"/>
        <transition on="success" to="captchaForm"/>
        <transition on="error" to="initializeLoginForm"/>
    </action-state>

    <!-- 认证成功后记录信息并生成ticket  com.demo.action.LoginStateAction-->
    <action-state id="loginState">
        <evaluate expression="loginStateAction"/>
        <transition on="success" to="sendTicketGrantingTicket"/>
    </action-state>
</flow>
package com.demo.sso.service.impl;

import com.demo.sso.entity.OutputViewConfig;
import com.demo.sso.service.IViewConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apereo.cas.web.support.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.ThemeResolver;
import org.springframework.webflow.execution.RequestContext;

/**
 * 页面配置项
 */
@Slf4j
@Service("viewConfigServiceImpl")
public class ViewConfigServiceImpl implements IViewConfigService {

    @Autowired
    private ThemeResolver themeResolver;

    @Override
    public OutputViewConfig getConfig(RequestContext requestContext) {
        OutputViewConfig config = new OutputViewConfig();
        // 获取当前登录的系统地址
        String serviceUrl = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext).getParameter("service");
        // 获取当前系统使用的主题
        String themeName = themeResolver.resolveThemeName(WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext));
        config.setPageType(themeName);

        log.info("==>serviceUrl:{},themeName:{}", serviceUrl, themeName);
        return config;
    }

    // 使用方法
    //  <input id="qrcodeType" th:value="${outputViewConfig.pageType}" style="display: none">

}
package com.demo.sso.action;

import org.apache.commons.lang3.StringUtils;
import org.apereo.cas.web.support.WebUtils;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.stereotype.Component;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * 自定义验证码逻辑
 */
@Component
public class CaptchaAction extends AbstractAction {

    @Override
    protected Event doExecute(final RequestContext requestContext) {
        // 是否需要验证码,true:需要,false:不需要,null:错误的直接提示“账号或密码错误”
        Boolean needVerificationCode = null;

        String username = StringUtils.EMPTY;
        String password = StringUtils.EMPTY;
        try {
            final HttpServletRequest request = WebUtils.getHttpServletRequestFromExternalWebflowContext(requestContext);
            Map<String, String[]> parameterMap = request.getParameterMap();
            username = parameterMap.containsKey("username") ? request.getParameter("username") : StringUtils.EMPTY;
            password = parameterMap.containsKey("password") ? request.getParameter("password") : StringUtils.EMPTY;

            // todo 业务逻辑


        } catch (Exception e) {

        }

        if (ssoCacheService.getAccountLockStatus(username)) {
            final MessageContext messageContext = requestContext.getMessageContext();
            // 当前账号已被锁定
            messageContext.addMessage(new MessageBuilder().error().defaultText("当前账号已被锁定").build());
            return this.error();
        }

        if (needVerificationCode == null) {
            final MessageContext messageContext = requestContext.getMessageContext();
            // 这里是页面弹窗提示信息
            messageContext.addMessage(new MessageBuilder().error().defaultText("账号或密码错误").build());
            return this.error();
        }

        return needVerificationCode ? new Event(this, "needCaptcha") : this.success();
    }
}
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐