Spring Security实现验证码登录功能

网友投稿 248 2022-12-16


Spring Security实现验证码登录功能

这篇文章主要介绍了Spring Security实现验证码登录功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

在spring security实现登录注销功能的基础上进行开发。

1、添加生成验证码的控制器。

(1)、生成验证码

/**

* 引入 Security 配置属性类

*/

@Autowired

private SecurityProperties securityProperties;

@Override

public ImageCode createCode(HttpServletRequest request ) {

//如果请求中有 width 参数,则用请求中的,否则用 配置属性中的

int width = ServletRequestUtils.getIntParameter(request,"width",securityProperties.getWidth());

//高度(宽度)

int height = ServletRequestUtils.getIntParameter(request,"height",securityProperties.getHeight());

//图片验证码字符个数

int length = securityProperties.getLength();

//过期时间

int expireIn = securityProperties.getExpireIn();

BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

Graphics g = image.getGraphics();

Random random = new Random();

g.setColor(getRandColor(200, 250));

g.fillRect(0, 0, width, height);

g.setFont(new Font("Times New Roman", Font.ITALIC, 20));

g.setColor(getRandColor(160, 200));

for (int i = 0; i < 155; i++) {

int x = random.nextInt(width);

int y = random.nextInt(height);

int xl = random.nextInt(12);

int yl = random.nextInt(12);

g.drawLine(x, y, x + xl, y + yl);

}

String sRand = "";

for (int i = 0; i < length; i++) {

String rand = String.valueOf(random.nextInt(10));

sRand += rand;

g.setColor(new Color(20 + random.nextInt(110), 20 + random.nextInt(110), 20 + random.nextInt(110)));

g.drawString(rand, 13 * i + 6, 16);

}

g.dispose();

return new ImageCode(image, sRand, expireIn);

}

/**

* 生成随机背景条纹

*/

private Color getRandColor(int fc, int bc) {

Random random = new Random();

if (fc > 255) {

fc = 255;

}

if (bc > 255) {

bc = 255;

}

int r = fc + random.nextInt(bc - fc);

int g = fc + random.nextInt(bc - fc);

int b = fc + random.nextInt(bc - fc);

return new Color(r, g, b);

}

(2)、验证码控制器

public static final String SESSION_KEY = "SESSION_KEY_IMAGE_CODE";

@Autowired

private ValidateCodeGenerator imageCodeGenerator;

/**

* Session 对象

*/

private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

@GetMapping("/code/image")

public void createCode(HttpServletRequest request, HttpServletResponse response) throws IOException {

ImageCode imageCode = imageCodeGenerator.createCode(request);

//将随机数 放到Session中

sessionStrategy.setAttribute(new ServletWebRequest(request),SESSION_KEY,imageCode);

request.getSession().setAttribute(SESSION_KEY,imageCode);

//写给response 响应

response.setHeader("Cache-Control", "no-store, no-cache");

response.setContentType("image/jpeg");

ImageIO.write(imageCode.getImage(),"JPEG",response.getOutputStream());

}

(3)、其它辅助类

@Data

public class ImageCode {

/**

* 图片

*/

private BufferedImage image;

/**

* 随机数

*/

private String code;

/**

* 过期时间

*/

private LocalDateTime expireTime;

public ImageCode(BufferedImage image, String code, LocalDateTime expireTime) {

this.image = image;

this.code = code;

this.expireTime = expireTime;

}

public ImageCode(BufferedImage image, String code, int expireIn) {

this.image = image;

this.code = code;

//当前时间 加上 设置过期的时间

this.expireTime = LocalDateTime.now().plusSeconds(expireIn);

}

public boolean isExpried(){

//如果 过期时间 在 当前日期 之前,则验证码过期

return LocalDateTime.now().isAfter(expireTime);

}

}

@ConfigurationProperties(prefix = "sso.security.code.image")

@Component

@Data

public class SecurityProperties {

/**

* 验证码宽度

*/

private int width = 67;

/**

* 高度

*/

private int height = 23;

/**

* 长度(几个数字)

*/

private int length = 4;

/**

* 过期时间

*/

private int expireIn = 60;

/**

* 需要图形验证码的 url

*/

private String url;

}

(4)、验证

2、添加过滤器,进行验证码验证

@Component

@Slf4j

public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean {

/**

* 登录失败处理器

*/

@Autowired

private AuthenticationFailureHandler failureHandler;

/**

* Session 对象

*/

private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

/**

* 创建一个Set 集合 存放 需要验证码的 urls

*/

private Set urls = new HashSet<>();

/**

* spring的一个工具类:用来判断 两字符串 是否匹配

*/

private AntPathMatcher pathMatcher = new AntPathMatcher();

@Autowired

private SecurityProperties securityProperties;

/**

* 这个方法是 InitializingBean 接口下的一个方法, 在初始化配置完成后 运行此方法

*/

@Override

public void afterPropertiesSet() throws ServletException {

super.afterPropertiesSet();

//将 application 配置中的 url 属性进行 切割

String[] configUrls = StringUtils.splitByWholeSeparatorPreserveAllTokens(securityProperties.getUrl(), ",");

//添加到 Set 集合里

urls.addAll(Arrays.asList(configUrls));

//因为登录请求一定要有验证码 ,所以直接 add 到set 集合中

urls.add("/authentication/form");

}

@Override

protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {

boolean action = false;

for (String url:urls){

//如果请求的url 和 配置中的url 相匹配

if (pathMatcher.match(url,httpServletRequest.getRequestURI())){

action = true;

}

}

//拦截请求

if (action){

logger.info("拦截成功"+httpServletRequest.getRequestURI());

//如果是登录请求

try {

validate(new ServletWebRequest(httpServletRequest));

}catch (ValidateCodeException exception){

//返回错误信息给 失败处理器

failureHandler.onAuthenticationFailure(httpServletRequest,httpServletResponse,exception);

return;

}

}

filterChain.doFilter(httpServletRequest,httpServletResponse);

}

private void validate(ServletWebRequest request) throws ServletRequestBindingException {

//从session中取出 验证码

ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request,ValidateCodeController.SESSION_KEY);

//从request 请求中 取出 验证码

String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),"imageCode");

if (StringUtils.isBlank(codeInRequest)){

logger.info("验证码不能为空");

throw new ValidateCodeException("验证码不能为空");

}

if (codeInSession == null){

logger.info("验证码不存在");

throw new ValidateCodeException("验证码不存在");

}

if (codeInSession.isExpried()){

logger.info("验证码已过期");

sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);

throw new ValidateCodeException("验证码已过期");

}

if (!StringUtils.equals(codeInSession.getCode(),codeInRequest)){

logger.info("验证码不匹配"+"codeInSession:"+codeInSession.getCode() +", codeInRequest:"+codeInRequest);

throw new ValidateCodeException("验证码不匹配");

}

//把对应 的 session信息 删掉

sessionStrategy.removeAttribute(request,ValidateCodeController.SESSION_KEY);

}

3、在核心配置BrowserSecurityConfig中添加过滤器配置

@Autowired

private ValidateCodeFilter validateCodeFilter;

@Override

protected void configure(HttpSecurity http) throws Exception {

//在UsernamePasswordAuthenticationFilter 过滤器前 加一个过滤器 来搞验证码

http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)

//表单登录 方式

.formLogin()

.loginPage("/authentication/require")

//登录需要经过的url请求

.loginProcessingUrl("/authentication/form")

.passwordParameter("pwd")

.usernameParameter("user")

.successHandler(mySuccessHandler)

.failureHandler(myFailHandler)

.and()

//请求授权

.authorizeRequests()

//不需要权限认证的url

.antMatchers("/authentication/*","/code/image").permitAll()

//任何请求

.anyRequest()

//需要身份认证

.authenticated()

.and()

//关闭跨站请求防护

.csrf().disable();

//默认注销地址:/logout

http.logout().

//注销之后 跳转的页面

logoutSuccessUrl("/authentication/require");

}

4、异常辅助类

public class ValidateCodeException extends AuthenticationException {

public ValidateCodeException(String msg, Throwable t) {

super(msg, t);

}

public ValidateCodeException(String msg) {

super(msg);

}

}

5、测试

(1)、不输入验证码

(2)、添加验证码


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring实战之使用TransactionProxyFactoryBean实现声明式事务操作示例
下一篇:基于idea把springboot项目部署到docker
相关文章

 发表评论

暂时没有评论,来抢沙发吧~