效果图
软件环境
apollo配置中心,官方下载地址: https://github.com/apolloconfig/apollo
apollo配置中心,官方文档:Apollo
验证码工具:hutool-all 5.8.36 官方网址:https://github.com/chinabugotech/hutool
需求说明
1、登陆页面添加验证码支持
2、验证码正确才能登陆成功
3、验证码为空或不正确需在登陆页面提示出来
4、不能影响现有功能和性能
5、验证码获取与校验需要在0.5s内完成
登陆页面添加验证码
二开login.html
<!-- 在密码表单的div后面添加如下代码 -->
<div class="form-group"><input type="text" name="captcha" tabindex="3"class="form-control" placeholder="Enter Captcha"><img src="/createCaptcha" alt="Captcha" />
</div>
maven配置
pom.xml
说明:
不做表单密码加密传输的不需要升级bcprov,表单密码加密传输见作者另一篇文章
Apollo配置中心登陆页面表单密码加密提交-CSDN博客
<!-- 工具包,包含验证码、加解密(无底层实现) -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.36</version>
</dependency><!-- 加解密(底层实现),低版本的可能需要升级 -->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk18on</artifactId><version>1.80</version>
</dependency>
小知识:
一般bcprov包并非直接引用,升级自带加解密包时可从对应依赖中先排除 ,然后添加新版本pom依赖。
二开Filter
二开或添加新的Filter皆可
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletRequest httpRequest = (HttpServletRequest) request;HttpServletResponse httpResponse = (HttpServletResponse) response;....... // 其他代码// 验证码支持if(isLoginRequest2(httpRequest, httpResponse)){String captchaCode1 = obtainCaptchaCode1(httpRequest );String captcha = obtainCaptchaCode( httpRequest );if(StringUtil.isEmpty(captcha)){httpResponse.sendRedirect("signin#/error");return;}if(!captcha.equalsIgnoreCase(captchaCode1)){httpResponse.sendRedirect("signin#/error");return;}}....... // 其他代码chain.doFilter(request, response);
}/*** 判断是否登陆请求* * @param request* @param response* @return*/
private boolean isLoginRequest2(HttpServletRequest request, HttpServletResponse response) {String url = request.getRequestURI();if(url.endsWith("signin") && "POST".equalsIgnoreCase(request.getMethod())){return true;}else{return false;}
}/*** 从session中取captchaCode1属性(登陆页面初始化时记录)* @param request* @return*/
protected String obtainCaptchaCode1(HttpServletRequest request) {return (String)request.getSession().getAttribute("captchaCode1" );
}/*** 从表单参数中取captcha* @param request* @return*/
protected String obtainCaptchaCode(HttpServletRequest request) {return request.getParameter("captcha" );
}
验证码生成Controller
可在已有的登陆Controller类中添加createCaptcha(创建验证码)方法
import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;/*** @author brickerman 2025-04-03*/
@Controller
public class SignInController {......//其他方法/*** 用于登陆页面初始化时创建验证码,并记录在 session 中* @param request* @param response* @throws IOException*/@RequestMapping(value = "/createCaptcha", method = RequestMethod.GET)public void createCaptcha(HttpServletRequest request, HttpServletResponse response) throws IOException {// 设置响应的内容类型为PNGresponse.setContentType(MediaType.IMAGE_PNG_VALUE);// 创建一个线型验证码对象,宽130,高50,4个字符长度LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(130, 50, 4, 5);// 获取验证码文本String captchaCode = lineCaptcha.getCode();System.out.println("验证码内容: " + captchaCode);request.getSession().setAttribute("captchaCode1", captchaCode);javax.servlet.ServletOutputStream out = response.getOutputStream();// 获取验证码图片BufferedImageBufferedImage image = lineCaptcha.getImage();// 将图片输出到文件(例如:captcha.png)ImageIO.write(image, "PNG", out);image.flush();response.flushBuffer(); // 确保数据被发送到客户端}
}
验证码请求不鉴权
找到权限配置类,如:AuthConfiguration(不同的版本可能不一样)
// 举例
@Configuration
public class AuthConfiguration {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable();http.headers().frameOptions().sameOrigin();http.authorizeRequests().antMatchers("/openapi/**", "/vendor/**","/styles/**", "/scripts/**","/views/**", "/img/**","/prometheus","/createCaptcha") // 取消验证码请求鉴权.permitAll().antMatchers("/**").hasAnyRole(USER_ROLE);http.formLogin().loginPage("/signin").permitAll().failureUrl("/signin?#/error").and().addFilter(authenticationFilter());http.logout().invalidateHttpSession(true).clearAuthentication(true).logoutSuccessUrl("/signin?#/logout");http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/signin"));}
}
优化登陆失败提示
这里把“用户名或密码错误”改为:“用户名或密码或验证码错误”
// LoginController.js
login_module.controller('LoginController',['$scope', '$window', '$location', 'toastr', 'AppUtil',LoginController]);function LoginController($scope, $window, $location, toastr, AppUtil) {if ($location.$$url) {var params = AppUtil.parseParams($location.$$url);if (params.error) {$scope.info = "用户名或密码或验证码错误";}if (params.logout) {$scope.info = "登出成功";}}
}
失败效果:
附件一:Apollo鉴权机制
Apollo鉴权机制主要包括两种实现方式:使用Apollo提供的Spring Security简单认证和接入公司的统一登录认证系统。
Apollo提供的Spring Security简单认证
Apollo从0.9.0版本开始提供了利用Spring Security实现的Http Basic简单认证。这种方式适用于那些没有统一登录认证系统的公司,实现起来相对简单。通过这种方式,Apollo配置中心默认使用Apollo账号进行鉴权,账号默认为“apollo”。
接入公司的统一登录认证系统
如果公司已经有统一的登录认证系统,如SSO、LDAP等,可以通过接入这些系统来实现鉴权。具体实现需要实现UserService和UserInfoHolder等接口,将Apollo配置中心与公司的认证系统集成。这种方式可以充分利用公司现有的认证资源,提高系统的安全性和管理效率。
配置和修改鉴权方式
如果需要修改或取消鉴权,可以通过修改配置文件来实现。例如,可以通过修改spring.profiles.active参数来启用或禁用鉴权。默认情况下,如果配置文件中没有指定鉴权方式,Apollo会使用默认的Apollo账号进行鉴权。如果需要取消鉴权,可以将配置文件中的auth配置删除即可。
附件二:Apollo集成公司SSO统一认证实现方案
一、实现原理
Apollo通过Portal模块的SPI扩展机制支持SSO集成,其核心逻辑是通过自定义Filter拦截请求,将认证流程交由企业SSO系统完成58。完成认证后,用户凭证信息会通过Cookie或分布式Session保存,最终通过UserInfoHolder
接口获取当前登录用户信息。
二、具体实现步骤
-
配置SSO客户端依赖
引入公司SSO系统提供的客户端JAR包,例如CAS Client或OAuth2 Client58。 -
实现认证Filter链
配置SSO Filter拦截/signin
等关键路径,实现以下逻辑:- 验证票据有效性后,将用户信息写入本地Session或Redis
- 未登录时重定向至SSO登录页面,并在回调时携带认证票据
- 检查请求是否携带有效凭证(如Token或SessionID)
-
实现用户信息接口
自定义UserService
接口实现类,从SSO系统获取用户基础信息(如用户ID、部门等)6- 实现
UserInfoHolder
接口,通过ThreadLocal存储当前用户上下文
- 实现
-
配置Apollo Profile
修改application.properties
,激活SSO Profile(如spring.profiles.active=auth
)- 禁用内置Spring Security认证(移除
auth
Profile相关配置)
- 禁用内置Spring Security认证(移除
-
权限映射同步
- 将SSO系统的角色/权限体系与Apollo权限模型(项目管理员、环境发布权限等)进行映射
- 通过
/user-manage.html
同步SSO用户到Apollo本地数据库
三、关键配置示例
# application-sso.properties
sso.enabled=true
sso.login-url=https://sso.company.com/login
sso.callback-url=https://apollo.company.com/callback
sso.token-header=X-Auth-Token
四、注意事项
-
跨域问题
需在SSO服务端配置Apollo Portal域名白名单,避免CORS拦截 -
会话管理
推荐使用Redis存储分布式Session,避免节点间状态不一致 -
高可用设计
SSO认证服务需部署集群,避免单点故障导致Apollo不可用 -
权限控制
Apollo默认采用应用负责人机制,需通过UserService
实现SSO用户与负责人自动关联 -
安全加固
在SSO侧启用MFA(多因素认证),提升Apollo管理界面访问安全性
五、验证流程
- 访问
https://apollo.company.com
触发SSO重定向 - 在SSO登录页完成认证后跳转回Apollo
- 检查浏览器控制台无
401 Unauthorized
错误 - 验证
UserInfoHolder.getUser()
能正确返回SSO用户信息
通过以上步骤,可实现Apollo与企业SSO系统的无缝集成,同时满足统一身份管理和权限控制需求
附件三:登陆表单密码加密
Apollo配置中心登陆页面表单密码加密提交-CSDN博客