后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载导读GUAGraphical User Authentication图形用户认证也叫登录图片login images是 Apereo CAS 提供的一种轻量级第二因子验证方案用户在创建账户时预选一张图片作为账户秘密登录时 CAS 先让用户输入用户名然后展示该用户预设的图片并要求用户继续输入密码——如果展示的图片与用户记忆中预选的图片不一致用户应拒绝提交剩余凭据从而有效抵御钓鱼网站冒充合法站点。本文以 GUA-Authentication.md 为主体结合仓库中cas-server-support-gua模块的源码、配置模型与测试用例完整讲解 GUA 的原理、启用方式、静态资源Resource与 LDAP 两种图片存储方案的配置以及底层 Webflow 登录流程的实现细节。GUA 是什么把图片变成账户秘密图形用户认证在本质上是一种共享秘密shared secret式的登录校验手段。它的核心思想是在账户创建阶段用户从站点提供的图片池中预先选择一张属于自己的图片这张图片与用户名绑定作为账户的专属标识登录时站点只要求用户输入用户名随即返回一页展示该用户名对应的预选图片同时提供密码输入框用户被训练为只有当页面展示的图片确实是自己当初选择的那张时才继续提交密码否则应当立即停止操作并离开该站点。正如 GUA-Authentication.md 中所强调的图片是与用户名绑定的账户秘密an account secret tied to the username它不应该被钓鱼攻击轻易复现should not be easily reproduced by a phishing campaign attempting to impersonate a legitimate website。钓鱼网站即使伪造出与合法站点完全一致的登录页面也无法预知受害者预选了哪张图片因此无法通过图片确认这一关。需要说明的是GUA 通常被定位为第二因子second factor性质的校验它不替代用户名密码认证而是在正式认证之前增加一道图片确认环节属于 认证Authentication 类别下的可选增强能力。启用 GUA引入 cas-server-support-gua 模块GUA 支持通过在 CAS overlay 项目中引入以下模块来启用Gradle 坐标来自官方文档的模块说明implementation org.apereo.cas:cas-server-support-gua该模块在仓库中的物理位置为 support/cas-server-support-gua其自动配置入口为 CasGraphicalUserAuthenticationAutoConfiguration.java类上的注解体现了两个关键前提ConditionalOnFeatureEnabled(feature CasFeatureModule.FeatureCatalog.Authentication, module gua)GUA 属于 Authentication 特性域下的gua子特性需在全局特性开关启用时才会生效AutoConfigurationSpring Boot 自动装配模块引入后无需手动注册 Bean。模块引入后CAS 会在登录 Webflow 中自动注入 GUA 相关动作与状态详见下文登录流程一节。图片存储方案一静态资源Static Resource适用场景与原理静态资源方案 的定位非常明确主要用于演示demo和测试testing目的。它允许 CAS 把一个全局且静态的图片资源作为用户标识加载到登录流程中。全局且静态的含义是这种方式不区分图片的所属用户——配置中通过一个以用户名为键、图片资源路径为值的 Map 来为不同用户绑定不同的图片配置简单直接适合开发调试、演示环境和自动化测试但不适合生产环境大规模管理用户图片。配置方式在application.properties或 YAML中通过cas.authn.gua.simple配置项指定用户名与图片资源的映射cas.authn.gua.simple.casuserfile:/etc/cas/config/images/casuser.jpg cas.authn.gua.simple.aliceclasspath:images/alice.png cas.authn.gua.simple.bobhttps://example.org/images/bob.jpgcas.authn.gua.simple在配置模型中的定义见 GraphicalUserAuthenticationProperties.java/** * Locate GUA settings and images from a static image per user. * This is treated as a {link Map} where the key is the user id * and the value should be the graphical resource. */ private MapString, String simple new LinkedHashMap();也就是说simple是一个MapString, Stringkey 是用户标识value 是图片资源位置。value 支持 Spring 的资源定位语法包括前缀含义示例file:文件系统路径file:/etc/cas/images/casuser.jpgclasspath:类路径资源如打成 jar 内嵌的图片classpath:images/casuser.jpghttp(s)://远程 URL 资源https://example.org/images/casuser.jpg图片资源会被解析为 Spring 的Resource对象。从 CasGraphicalUserAuthenticationAutoConfiguration.java 可以看到当gua.getSimple()非空时自动配置会优先选择静态资源仓库Bean RefreshScope(proxyMode ScopedProxyMode.DEFAULT) ConditionalOnMissingBean(name userGraphicalAuthenticationRepository) public UserGraphicalAuthenticationRepository userGraphicalAuthenticationRepository( final CasConfigurationProperties casProperties) { val gua casProperties.getAuthn().getGua(); if (!gua.getSimple().isEmpty()) { val accounts gua.getSimple().entrySet().stream().map(Unchecked.function(entry - { val res ResourceUtils.getResourceFrom(entry.getValue()); return Pair.of(entry.getKey(), (Resource) res); })).collect(Collectors.toMap(Pair::getKey, Pair::getValue)); return new StaticUserGraphicalAuthenticationRepository(accounts); } // ...LDAP 分支... throw new BeanCreationException(A repository instance must be configured to locate user-defined graphics); }底层实现StaticUserGraphicalAuthenticationRepository静态图片的读取逻辑位于 StaticUserGraphicalAuthenticationRepository.java。它实现了统一的仓库接口 UserGraphicalAuthenticationRepository该接口为函数式接口核心方法只有一个ByteSource getGraphics(String username)Override public ByteSource getGraphics(final String username) { try (val resourceStream graphicResource.get(username).getInputStream(); val bos new ByteArrayOutputStream()) { IOUtils.copy(resourceStream, bos); return ByteSource.wrap(bos.toByteArray()); } catch (final Exception e) { LoggingUtils.error(LOGGER, e); } return ByteSource.empty(); }实现要点以用户名为 key 从MapString, Resource中取出图片资源读取为字节流并包装成 Guava 的ByteSource如果用户不存在、资源缺失或读取失败返回ByteSource.empty()不会抛出异常对应的单元测试 StaticUserGraphicalAuthenticationRepositoryTests.java 验证了存在图片返回非空与图片缺失返回空两种行为verifyImage以Map.of(casuser, new ClassPathResource(image.jpg))构造仓库断言getGraphics(casuser)非空verifyBadImage以不存在的missing.jpg构造断言返回空。在测试基类 AbstractGraphicalAuthenticationTests.java 中静态方案的实际配置写法为cas.authn.gua.simple.casuserclasspath:image.jpg这正好可以作为最小可运行的参考配置。图片存储方案二LDAP 二进制属性适用场景与原理LDAP 方案 允许 CAS 从 LDAP 目录中定位用户的二进制图片属性binary image attribute将该二进制属性值作为用户标识加载到登录流程。相比静态资源方案LDAP 方案把图片作为用户目录数据的一部分统一管理更贴近生产环境适合已有 LDAP/AD 目录的机构。配置方式LDAP 方案通过cas.authn.gua.ldap配置项启用。核心配置包括cas.authn.gua.ldap.ldap-urlldap://localhost:10389 cas.authn.gua.ldap.base-dndcexample,dcorg cas.authn.gua.ldap.search-filtercn{user} cas.authn.gua.ldap.image-attributejpegPhoto cas.authn.gua.ldap.bind-dncnDirectory Manager cas.authn.gua.ldap.bind-credentialpassword参数说明配置项是否必填说明cas.authn.gua.ldap.ldap-url是LDAP 服务器地址多个地址可用空格或逗号分隔支持ACTIVE_PASSIVE、ROUND_ROBIN、RANDOM、DNS_SRV等连接策略cas.authn.gua.ldap.base-dn是搜索的基础 DN可配置多个子树并用\|分隔如subtreeA,dcexample,dcnet\|subtreeC,dcexample,dcnetcas.authn.gua.ldap.search-filter是用户搜索过滤器语法为cn{user}或cn{0}也支持file:/path/to/GroovyScript.groovy外部脚本动态构造过滤器cas.authn.gua.ldap.image-attribute是存放用户图片的条目属性名如jpegPhoto该属性必须是二进制类型cas.authn.gua.ldap.bind-dn/bind-credential是连接 LDAP 的绑定凭据置空表示匿名操作设为*表示 fast-bind 策略cas.authn.gua.ldap.subtree-search否默认true是否允许子树搜索cas.authn.gua.ldap.page-size否分页请求大小用于规避服务器结果集大小限制负值/零值禁用分页cas.authn.gua.ldap.use-start-tls否默认false是否启用 StartTLScas.authn.gua.ldap.connect-timeout/response-timeout否默认PT5S连接与响应超时cas.authn.gua.ldap.trust-certificates、trust-store等否LDAPS/StartTLS 场景下的证书信任配置image-attribute是 LDAP 方案独有且必填的属性定义见 LdapGraphicalUserAuthenticationProperties.java/** * Entry attribute that holds the user image. */ RequiredProperty private String imageAttribute;其余 LDAP 搜索与连接参数继承自 AbstractLdapSearchProperties 与 AbstractLdapPropertiesCAS 其他 LDAP 相关模块认证、属性仓库、服务注册等也复用同一套参数体系。底层实现LdapUserGraphicalAuthenticationRepositoryLDAP 图片的检索逻辑位于 LdapUserGraphicalAuthenticationRepository.javaOverride public ByteSource getGraphics(final String username) { val gua casProperties.getAuthn().getGua(); val response searchForId(username); if (LdapUtils.containsResultEntry(response)) { val entry response.getEntry(); val attribute entry.getAttribute(gua.getLdap().getImageAttribute()); if (attribute ! null attribute.isBinary()) { return ByteSource.wrap(attribute.getBinaryValue()); } } return ByteSource.empty(); } private SearchResponse searchForId(final String id) { return FunctionUtils.doUnchecked(() - { val gua casProperties.getAuthn().getGua(); val filter LdapUtils.newLdaptiveSearchFilter(gua.getLdap().getSearchFilter(), LdapUtils.LDAP_SEARCH_FILTER_DEFAULT_PARAM_NAME, CollectionUtils.wrap(id)); return connectionFactory.executeSearchOperation( gua.getLdap().getBaseDn(), filter, gua.getLdap().getPageSize(), new String[]{gua.getLdap().getImageAttribute()}, ReturnAttributes.ALL_USER.value()); }); }实现要点使用配置中的searchFiltercn{user}形式与baseDn发起搜索返回属性限定为imageAttribute检索到条目后读取imageAttribute属性的二进制值attribute.isBinary()检查并包装为ByteSource若用户不存在或属性缺失同样返回空该类同时实现DisposableBean在销毁时关闭LdapConnectionFactory释放连接池资源从 CasGraphicalUserAuthenticationAutoConfiguration.java 可见LDAP 分支的启用条件是ldapUrl、searchFilter、baseDn、imageAttribute四个属性全部非空若simple与 LDAP 配置都不满足启动时抛出BeanCreationExceptionA repository instance must be configured to locate user-defined graphics。测试验证jpegPhoto 属性的端到端验证仓库中的集成测试 LdapUserGraphicalAuthenticationRepositoryTests.java 提供了完整的验证思路该测试依赖本机10389端口可用的 LDAP 服务由EnabledIfListeningOnPort(port 10389)控制使用与上面一致的 LDAP 配置初始化 Spring 上下文createLdapEntry方法向目录中添加一个inetOrgPerson条目其jpegPhoto属性写入image.jpg的字节内容断言getGraphics(cn)返回非空而getGraphics(bad-user)返回空。这从测试层面印证了LDAP 条目中的二进制图片属性典型如jpegPhoto就是 GUA 图片的承载介质目录管理员只需保证用户条目存在且图片属性为二进制格式即可。GUA 在登录 Webflow 中的位置三段式流程引入模块后GraphicalUserAuthenticationWebflowConfigurer.java 会把 GUA 流程织入 CAS 登录流程login flow整体流程为登录表单(initLoginForm) --GUA_PREPARE_LOGIN-- 输入用户名(casGuaGetUserIdView) -- 展示图片密码框(casGuaDisplayUserGraphicsView) -- 接受图片(AcceptUserGraphics) -- 回到原认证流程具体的织入逻辑doInitialize方法在initLoginForm状态的动作列表中前置追加ACTION_ID_GUA_PREPARE_LOGIN动作为登录表单状态新增一条GUA_GET_USERID转换指向新建的视图状态gua/casGuaGetUserIdView即先输用户名页面从用户名视图提交后进入gua/casGuaDisplayUserGraphicsView即展示图片密码页面并在该视图的渲染动作列表中注册DISPLAY_USER_GRAPHICS_BEFORE_AUTHENTICATION动作图片页提交后进入ACCEPT_GUA动作状态ACCEPT_USER动作随后默认转换回到原登录表单状态原本的 success 目标状态继续执行常规的 Username/Password 认证。三个核心 Action 的职责1. PrepareForGraphicalAuthenticationAction流程入口PrepareForGraphicalAuthenticationAction.java 在登录表单渲染前执行通过WebUtils.putGraphicalUserAuthenticationEnabled(requestContext, Boolean.TRUE)标记 GUA 已启用若当前流程上下文中还没有 GUA 用户名则触发GUA_GET_USERID转换把用户导向仅输入用户名的页面否则放行继续。2. DisplayUserGraphicsBeforeAuthenticationAction取图与校验DisplayUserGraphicsBeforeAuthenticationAction.java 是 GUA 的核心校验动作Override protected Nullable Event doExecuteInternal(final RequestContext requestContext) throws Exception { val username requestContext.getRequestParameters().get(username); if (StringUtils.isBlank(username)) { throw UnauthorizedServiceException.denied(Denied); } val graphics repository.getGraphics(username); if (graphics null || graphics.isEmpty()) { throw UnauthorizedServiceException.denied(Denied); } val image EncodingUtils.encodeBase64ToByteArray(graphics.read()); WebUtils.putGraphicalUserAuthenticationUsername(requestContext, username); WebUtils.putGraphicalUserAuthenticationImage(requestContext, new String(image, StandardCharsets.UTF_8)); return success(); }要点从请求参数中取出用户名为空直接抛出UnauthorizedServiceException.denied(Denied)调用仓库接口getGraphics(username)取图图片为空同样直接拒绝——这意味着用户名错误或没有绑定图片的用户无法通过 GUA 环节取图成功后将图片字节流做Base64 编码连同用户名一起放入 Webflow 请求上下文供视图层渲染。对应的测试 DisplayUserGraphicsBeforeAuthenticationActionTests.java 验证了正常返回 success 且上下文中包含图片与用户名以及缺少用户名时抛出 UnauthorizedServiceException两个分支。3. AcceptUserGraphicsForAuthenticationAction衔接主认证AcceptUserGraphicsForAuthenticationAction.java 在用户确认图片并提交密码页后执行以当前用户名构造UsernamePasswordCredential(username, null)放入 Webflow 上下文密码由后续密码页收集再次记录 GUA 用户名返回 success流程回到登录表单的原始 success 目标状态继续常规认证。从源码结构看GUA 的定位是主认证之前的图片确认闸门图片校验通过后认证依旧走标准的用户名密码路径因此 GUA 与 LDAP/JDBC/静态等认证处理器天然兼容。前端视图与多语言提示GUA 的两个页面视图由 thymeleaf 模块提供用户名输入页 casGuaGetUserIdView.html一个只含用户名字段的表单提交事件为_eventId_submit图片展示页 casGuaDisplayUserGraphicsView.htmlh2 classtext-center th:text${guaUsername}guaUsername/h2 div idguaInfo classbanner banner-danger alert alert-danger d-flex m-4 p-4 rolealert i classmdi mdi-alert-octagon fas fa-exclamation-circle aria-hiddentrue/i strong th:utext#{screen.gua.confirm.message} If you do not recognize this image as yours, do NOT continue./strong /div img idguaImage stylewidth:130px;height:130px; th:src{data:image/jpeg;base64, ${guaUserImage}} altUser graphic /值得注意的细节图片通过data:image/jpeg;base64,...内联 data URI渲染这正是前面DisplayUserGraphicsBeforeAuthenticationAction中 Base64 编码的原因——图片不经过额外 HTTP 请求直接内嵌在页面中页面在图片旁以醒目的警示样式展示提示语If you do not recognize this image as yours, do NOT continue.如果你不确认这张图片是你自己的请不要继续。这正是 GUA 对抗钓鱼的核心用户教育话术该提示语通过screen.gua.confirm.message国际化键管理仓库在 messages.properties 以及messages_zh_CN.properties、messages_fr.properties等 30 余种语言文件中均提供了翻译如中文如果您无法识别该图像是您的请不要继续。多语言环境开箱即用图片展示页同样提交_eventId_submitContinue由AcceptUserGraphicsForAuthenticationAction承接。配置速查与注意事项两种方案的启用逻辑总结方案配置前缀判定条件自动配置典型用途静态资源cas.authn.gua.simplesimpleMap 非空即启用演示、测试、开发调试LDAPcas.authn.gua.ldapldapUrl、searchFilter、baseDn、imageAttribute全部非空生产环境、已有目录服务两者的优先级见 CasGraphicalUserAuthenticationAutoConfiguration.java静态方案优先两者都未配置时启动直接失败并提示必须配置图片仓库。使用建议静态资源方案中的 value 建议优先使用file:绝对路径或classpath:资源避免生产环境对远程 URL 的强依赖LDAP 方案的image-attribute必须指向二进制类型的属性实现代码中显式检查了attribute.isBinary()最典型的是jpegPhotoOpenLDAP/389ds 等目录的常用照片属性图片展示页的img标签固定使用data:image/jpeg;base64前缀因此建议图片统一使用 JPEG 格式以兼容该内联渲染方式GUA 环节中用户名缺失或图片缺失都会被拒绝UnauthorizedServiceException.denied因此请确保启用 GUA 前所有目标用户的图片数据完整若同时启用 GUA 与其他登录方式GUA 仅作为登录流程的前置步骤不影响 CAS 既有的认证处理器、MFA 等后续环节的编排。小结GUA 是 Apereo CAS 提供的一种轻量、易落地的钓鱼防御手段它把预选图片转化为绑定用户名的账户秘密在密码提交前增加一道图片确认关卡。本文从官方文档出发结合仓库源码完整梳理了它的概念模型、模块引入方式、静态资源与 LDAP 两种图片存储方案的配置与底层实现仓库接口UserGraphicalAuthenticationRepository与两个实现类、Webflow 三段式登录流程GUA_PREPARE_LOGIN→DISPLAY_USER_GRAPHICS_BEFORE_AUTHENTICATION→ACCEPT_USER、Base64 内联渲染机制与多语言提示并给出了测试用例作为配置正确性的可验证依据。无论是搭建演示环境cas.authn.gua.simple还是对接生产目录cas.authn.gua.ldapjpegPhoto都可以参照上文配置快速落地。赞分享后端认证鉴权单点登录【免费下载链接】casApereo CAS - Identity Single Sign On for all earthlings and beyond.项目地址https://gitcode.com/gh_mirrors/ca/cas点击查看免费下载相关推荐Apereo CAS 静态资源图形用户认证GUA配置与源码原理指南Apereo CAS 静态资源图形用户认证GUA配置与源码原理指南 GUAGraphical User Authentication是 Apereo C后端认证鉴权单点登录Opik Python SDK GEval 指标详解LLM-as-Judge 通用评估指标的机制、参数与实战Opik Python SDK GEval 指标详解LLM as Judge 通用评估指标的机制、参数与实战 GEval 是 Opik Python SDK后端认证鉴权单点登录Apereo CAS 集成 Apache Cassandra 认证配置详解与源码剖析Apereo CAS 集成 Apache Cassandra 认证配置详解与源码剖析 本文基于 Cassandra Authentication.md htt后端认证鉴权单点登录上一篇Material 框架架构深度剖析从 ViewController 继承体系到 Themeable 协议的源码级解读下一篇TheRemoteFreelancer终极对比指南2025年远程工作平台深度评测创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
