系列博文

  项目已上传至guthub  传送门

  JavaWeb-SpringSecurity初认识  传送门

  JavaWeb-SpringSecurity在数据库中查询登陆用户  传送门

  JavaWeb-SpringSecurity自定义登陆页面  传送门

  JavaWeb-SpringSecurity实现需求-判断请求是否以html结尾  传送门

  JavaWeb-SpringSecurity自定义登陆配置  传送门

  JavaWeb-SpringSecurity图片验证ImageCode  传送门

  JavaWeb-SpringSecurity记住我功能  传送门

  JavaWeb-SpringSecurity使用短信验证码登陆  传送门

  创建一个validate.code存放编写验证码校验代码,创建ImageCode.class图片验证码工具类

  

package com.Gary.GaryRESTful.validate.code;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime; //图片验证码
public class ImageCode { //给前台展示的图片
private BufferedImage image; //验证码
private String code; //过期时间
private LocalDateTime expireTime; public ImageCode(BufferedImage image,String code,int expreTime)
{
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expreTime);
} public ImageCode(BufferedImage image,String code,LocalDateTime expireTime)
{
this.image = image;
this.code = code;
this.expireTime = expireTime;
} public BufferedImage getImage() {
return image;
} public void setImage(BufferedImage image) {
this.image = image;
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public LocalDateTime getExpireTime() {
return expireTime;
} public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
} }

ImageCode.class

  在controller层中创建ValidateCodeController.class

  图片验证三步骤

@GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
{
//生成随机数的图片
ImageCode imageCode = createImageCode(); //将随机数放入到session中
sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode); //将我们生成的图片写到接口的响应的输出流中
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream()); }
package com.Gary.GaryRESTful.controller;

import java.io.IOException;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import com.Gary.GaryRESTful.validate.code.ImageCode; @RestController
public class ValidateCodeController { //操作Session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); private String sessionKey = "session_key_image_code"; @GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
{
//生成随机数的图片
ImageCode imageCode = createImageCode(); //将随机数放入到session中
sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode); //将我们生成的图片写到接口的响应的输出流中
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream()); } //生成图片验证码(验证码,图片,失效的时间)
private ImageCode createImageCode()
{
return null;
} }

ValidateCodeController.class

  编写验证码的逻辑

    1、定义验证码图片长与宽

    2、获取画笔工具

    3、设置画笔工具的颜色

    4、画长方形

    5、改变画笔工具的颜色

    6、画干扰线

    7、改变画笔工具的颜色

    8、画数据

    9、关闭画笔工具

  登陆页面login.html

<form action="/loginPage" method="post">

        用户名:
<input type="text" name="username">
<br>
密码:
<input type="password" name="password">
<br>
图片验证码:
<input type="text" name="imageCode">
<img src="/code/image">
<input type="submit"> </form>

  ValidateCodeController.java中实现绘画验证码方法createImageCode()

//生成图片验证码(验证码,图片,失效的时间)
private ImageCode createImageCode()
{
//定义图片的长和宽
int width = 67;
int height = 23; //生成一张图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //获得画笔工具
Graphics g = image.getGraphics(); //画一个矩形
g.setColor(new Color(255,255,255));
g.fillRect(0, 0, width, height); //画干扰线
g.setColor(new Color(0,0,0));
//设置字体
g.setFont(new Font("Time New Roman",Font.ITALIC,20));
Random random = new Random(); for(int i=0;i<20;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(12);
int y1 = random.nextInt(12);
//(x,y)到(x+x1,y+y1)
g.drawLine(x, y, x+x1, y+y1);
} //画数据
String sRand = "";
for(int i = 0;i<4;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, 16);
} g.dispose(); //生成我们自己的验证码数据(图片,验证码,过期时间)
return new ImageCode(image,sRand,60);
}
package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random; import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import com.Gary.GaryRESTful.validate.code.ImageCode; @RestController
public class ValidateCodeController { //操作Session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); private String sessionKey = "session_key_image_code"; @GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
{
//生成随机数的图片
ImageCode imageCode = createImageCode(); //将随机数放入到session中
sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode); //将我们生成的图片写到接口的响应的输出流中
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream()); } //生成图片验证码(验证码,图片,失效的时间)
private ImageCode createImageCode()
{
//定义图片的长和宽
int width = 67;
int height = 23; //生成一张图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //获得画笔工具
Graphics g = image.getGraphics(); //画一个矩形
g.setColor(new Color(255,255,255));
g.fillRect(0, 0, width, height); //画干扰线
g.setColor(new Color(0,0,0));
//设置字体
g.setFont(new Font("Time New Roman",Font.ITALIC,20));
Random random = new Random(); for(int i=0;i<20;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(12);
int y1 = random.nextInt(12);
//(x,y)到(x+x1,y+y1)
g.drawLine(x, y, x+x1, y+y1);
} //画数据
String sRand = "";
for(int i = 0;i<4;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, 16);
} g.dispose(); //生成我们自己的验证码数据(图片,验证码,过期时间)
return new ImageCode(image,sRand,60);
} }

ValidateCodeController.java

  处理验证码拦截器,添加处理图片验证码的Filter

  如果验证码登陆成功,则放行,否则进行拦截

  配置验证码拦截器ValidateCodeException.java,继承AuthenticationException.java

//AuthenticationException是springsecurity中所有异常的基类
public class ValidateCodeException extends AuthenticationException{ public ValidateCodeException(String msg) {
super(msg);
// TODO Auto-generated constructor stub
} }

  检验验证码是否正确方法

//校验验证码是否正确
private void validate(ServletWebRequest request) throws ServletRequestBindingException { // 获得session域中正确的验证码
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
// 获得request域中的用户输入的验证码imageCode
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode"); // 判断用户输入的验证码是否为空
if(StringUtils.isEmpty(codeInRequest))
{
throw new ValidateCodeException("验证码不能为空");
} // 判断session域中的验证码是否为null
if(codeInSession == null)
{
throw new ValidateCodeException("验证码不存在");
} // 判断验证码是否过期
if(codeInSession.isExpried())
{
throw new ValidateCodeException("验证码已过期");
} // 校验两个验证码是否匹配
if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
{
//System.out.println("正确的:"+codeInSession.getCode());
//System.out.println("用户输入的:"+codeInRequest);
throw new ValidateCodeException("验证码不匹配");
} // 将验证码从session域中移除
sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey); }

  如果用户验证码正确则放行用户登陆步骤,当用户登陆输入正确输入账号密码时,则给与用户下一步操作,否则返回"坏的凭证"

  验证码如果没有输入正确,不会放行用户登陆步骤

  SecurityConfig.java配置

//Web应用安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{ //告诉SpringSecurity密码用什么加密的
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
} @Autowired
private LoginSuccessHandler loginSuccessHandler; @Autowired
private LoginFailureHandler loginFailureHandler; protected void configure(HttpSecurity http) throws Exception{ //声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
//给过滤器赋值
validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler); //表单验证(身份认证)
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin()
//自定义登陆页面
.loginPage("/require")
//如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
.loginProcessingUrl("/loginPage")
//配置登陆成功调用loginSuccessHandler
.successHandler(loginSuccessHandler)
//配置登陆失败调用loginFailureHandler
.failureHandler(loginFailureHandler)
.and()
//请求授权
.authorizeRequests()
//在访问我们的URL时,我们是不需要省份认证,可以立即访问
.antMatchers("/login.html","/require","/code/image").permitAll()
//所有请求都被拦截,跳转到(/login请求中)
.anyRequest()
//都需要我们身份认证
.authenticated()
//SpringSecurity保护机制
.and().csrf().disable();
}

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body> <h1>Gary登陆页面</h1>
<form action="/loginPage" method="post"> 用户名:
<input type="text" name="username">
<br>
密码:
<input type="password" name="password">
<br>
图片验证码:
<input type="text" name="imageCode">
<img src="/code/image">
<input type="submit"> </form> </body>
</html>

login.html

package com.Gary.GaryRESTful.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
import com.Gary.GaryRESTful.handler.LoginFailureHandler;
import com.Gary.GaryRESTful.handler.LoginSuccessHandler; //Web应用安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{ //告诉SpringSecurity密码用什么加密的
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
} @Autowired
private LoginSuccessHandler loginSuccessHandler; @Autowired
private LoginFailureHandler loginFailureHandler; protected void configure(HttpSecurity http) throws Exception{ //声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
//给过滤器赋值
validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler); //表单验证(身份认证)
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin()
//自定义登陆页面
.loginPage("/require")
//如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
.loginProcessingUrl("/loginPage")
//配置登陆成功调用loginSuccessHandler
.successHandler(loginSuccessHandler)
//配置登陆失败调用loginFailureHandler
.failureHandler(loginFailureHandler)
.and()
//请求授权
.authorizeRequests()
//在访问我们的URL时,我们是不需要省份认证,可以立即访问
.antMatchers("/login.html","/require","/code/image").permitAll()
//所有请求都被拦截,跳转到(/login请求中)
.anyRequest()
//都需要我们身份认证
.authenticated()
//SpringSecurity保护机制
.and().csrf().disable();
} }

SecurityConfig.java

package com.Gary.GaryRESTful.validate.code;

import java.awt.image.BufferedImage;
import java.time.LocalDateTime; //图片验证码
public class ImageCode { //给前台展示的图片
private BufferedImage image; //验证码
private String code; //过期时间
private LocalDateTime expireTime; public ImageCode(BufferedImage image,String code,int expreTime)
{
this.image = image;
this.code = code;
this.expireTime = LocalDateTime.now().plusSeconds(expreTime);
} public ImageCode(BufferedImage image,String code,LocalDateTime expireTime)
{
this.image = image;
this.code = code;
this.expireTime = expireTime;
} //判断验证码是否过去
public boolean isExpried()
{
//判断当前时间是否在过期时间之后
return LocalDateTime.now().isAfter(expireTime);
} public BufferedImage getImage() {
return image;
} public void setImage(BufferedImage image) {
this.image = image;
} public String getCode() {
return code;
} public void setCode(String code) {
this.code = code;
} public LocalDateTime getExpireTime() {
return expireTime;
} public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
} }

ImageCode.java

package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random; import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import com.Gary.GaryRESTful.validate.code.ImageCode; @RestController
public class ValidateCodeController { //操作Session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); public SessionStrategy getSessionStrategy() {
return sessionStrategy;
} public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
} public static String getSessionKey() {
return sessionKey;
} public static void setSessionKey(String sessionKey) {
ValidateCodeController.sessionKey = sessionKey;
} public static String sessionKey = "session_key_image_code"; @GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
{
//生成随机数的图片
ImageCode imageCode = createImageCode(); //将随机数放入到session中
sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode); //将我们生成的图片写到接口的响应的输出流中
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream()); } //生成图片验证码(验证码,图片,失效的时间)
private ImageCode createImageCode()
{
//定义图片的长和宽
int width = 67;
int height = 23; //生成一张图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //获得画笔工具
Graphics g = image.getGraphics(); //画一个矩形
g.setColor(new Color(255,255,255));
g.fillRect(0, 0, width, height); //画干扰线
g.setColor(new Color(0,0,0));
//设置字体
g.setFont(new Font("Time New Roman",Font.ITALIC,20));
Random random = new Random(); for(int i=0;i<20;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(12);
int y1 = random.nextInt(12);
//(x,y)到(x+x1,y+y1)
g.drawLine(x, y, x+x1, y+y1);
} //画数据
String sRand = "";
for(int i = 0;i<4;i++)
{
String rand =String.valueOf(random.nextInt(10));
//System.out.println(rand);
sRand += rand;
//每一个字都改变一下颜色
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
//画每一个数据
g.drawString(rand, 13*i, 16);
} g.dispose(); //生成我们自己的验证码数据(图片,验证码,过期时间)
return new ImageCode(image,sRand,60000);
} }

ValidateCodeController.java

package com.Gary.GaryRESTful.exception;

import org.springframework.security.core.AuthenticationException;

//AuthenticationException是springsecurity中所有异常的基类
public class ValidateCodeException extends AuthenticationException{ public ValidateCodeException(String msg) {
super(msg);
// TODO Auto-generated constructor stub
} }

ValidateCodeException.java

package com.Gary.GaryRESTful.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter; import com.Gary.GaryRESTful.controller.ValidateCodeController;
import com.Gary.GaryRESTful.exception.ValidateCodeException;
import com.Gary.GaryRESTful.validate.code.ImageCode; //继承OncePerRequestFilter,保证这个filter只会执行一次
public class ValidateCodeFilter extends OncePerRequestFilter{ private AuthenticationFailureHandler authenticationFailureHandler; //操作session域的工具
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); //Filter执行
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { //loginPage
if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post"))
{
//filter才会执行
try
{
validate(new ServletWebRequest(request));
}
catch(ValidateCodeException e) {
//判处验证码的异常
authenticationFailureHandler.onAuthenticationFailure(request,response,e);
//一旦出现异常,我们就不就不能继续执行(应该放行),应该return
return;
} } //放行
filterChain.doFilter(request, response); } //校验验证码是否正确
private void validate(ServletWebRequest request) throws ServletRequestBindingException { // 获得session域中正确的验证码
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
// 获得request域中的用户输入的验证码imageCode
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode"); // 判断用户输入的验证码是否为空
if(StringUtils.isEmpty(codeInRequest))
{
throw new ValidateCodeException("验证码不能为空");
} // 判断session域中的验证码是否为null
if(codeInSession == null)
{
throw new ValidateCodeException("验证码不存在");
} // 判断验证码是否过期
if(codeInSession.isExpried())
{
throw new ValidateCodeException("验证码已过期");
} // 校验两个验证码是否匹配
if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
{
//System.out.println("正确的:"+codeInSession.getCode());
//System.out.println("用户输入的:"+codeInRequest);
throw new ValidateCodeException("验证码不匹配");
} // 将验证码从session域中移除
sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey); } public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
} public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
} public SessionStrategy getSessionStrategy() {
return sessionStrategy;
} public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
} }

ValidateCodeFilter.java

优化:增加代码的重用性

  在GaryRESTful.properties包下创建ValidateCodeProperties.class,用于管理配置图片验证码的功能,再创建一个ImageCodeProperties.class,用于管理图片验证码的生成

  优化图片验证码的生成

  

  application.properties

#配置登陆方式
gary.security.loginType = JSON server.port=8081 #验证码长度
gary.security.code.image.length = 6
#验证码图片的长
gary.security.code.image.width = 100

#datasource
spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.dricer-class-name=com.mysql.jdbc.Driver #jpa
#打印出数据库语句
spring.jpa.show-sql=true
#更新数据库表
spring.jpa.hibernate.ddl-auto=update #配置登陆方式
gary.security.loginType = JSON server.port=8081 #验证码长度
gary.security.code.image.length = 6
#验证码图片的长
gary.security.code.image.width = 100

application.properties

package com.Gary.GaryRESTful.controller;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random; import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.ServletWebRequest; import com.Gary.GaryRESTful.properties.GarySecurityProperties;
import com.Gary.GaryRESTful.validate.code.ImageCode; @RestController
public class ValidateCodeController { //操作Session
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); public static String sessionKey = "session_key_image_code"; @Autowired
private GarySecurityProperties garySecurityProperties; public SessionStrategy getSessionStrategy() {
return sessionStrategy;
} public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
} public static String getSessionKey() {
return sessionKey;
} public static void setSessionKey(String sessionKey) {
ValidateCodeController.sessionKey = sessionKey;
} @GetMapping("/code/image")
public void createCode(HttpServletRequest request,HttpServletResponse response) throws IOException
{
//生成随机数的图片
ImageCode imageCode = createImageCode(request); //将随机数放入到session中
sessionStrategy.setAttribute(new ServletWebRequest(request), sessionKey, imageCode); //将我们生成的图片写到接口的响应的输出流中
ImageIO.write(imageCode.getImage(), "JPEG", response.getOutputStream()); } //生成图片验证码(验证码,图片,失效的时间)
private ImageCode createImageCode(HttpServletRequest request)
{
//定义图片的长和宽
int width = ServletRequestUtils.getIntParameter(request, "width", garySecurityProperties.getCode().getImage().getWidth());
int height = ServletRequestUtils.getIntParameter(request, "height", garySecurityProperties.getCode().getImage().getHeight());; //生成一张图片
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //获得画笔工具
Graphics g = image.getGraphics(); //画一个矩形
g.setColor(new Color(255,255,255));
g.fillRect(0, 0, width, height); //画干扰线
g.setColor(new Color(0,0,0));
//设置字体
g.setFont(new Font("Time New Roman",Font.ITALIC,20));
Random random = new Random(); for(int i=0;i<20;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int x1 = random.nextInt(12);
int y1 = random.nextInt(12);
//(x,y)到(x+x1,y+y1)
g.drawLine(x, y, x+x1, y+y1);
} //画数据
String sRand = "";
for(int i = 0;i<garySecurityProperties.getCode().getImage().getLength();i++)
{
String rand =String.valueOf(random.nextInt(10));
//System.out.println(rand);
sRand += rand;
//每一个字都改变一下颜色
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));
//画每一个数据
g.drawString(rand, 13*i, 16);
} g.dispose(); //生成我们自己的验证码数据(图片,验证码,过期时间)
return new ImageCode(image,sRand,garySecurityProperties.getCode().getImage().getExpireIn());
} }

ValidateCodeController.java

package com.Gary.GaryRESTful.properties;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "gary.security")
public class GarySecurityProperties { //LoginType登陆的方式,默认为JSON(restful设计风格)
private LoginType loginType = LoginType.JSON; private ValidateCodeProperties code = new ValidateCodeProperties(); public ValidateCodeProperties getCode() {
return code;
} public void setCode(ValidateCodeProperties code) {
this.code = code;
} public LoginType getLoginType() {
return loginType;
} public void setLoginType(LoginType loginType) {
this.loginType = loginType;
} }

GarySecurityProperties.java

package com.Gary.GaryRESTful.properties;

public class ImageCodeProperties {

    private int width = 67;
private int height = 23; private int length = 4;
private int expireIn = 60; public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
public int getExpireIn() {
return expireIn;
}
public void setExpireIn(int expireIn) {
this.expireIn = expireIn;
} }

ImageCodeProperties.java

package com.Gary.GaryRESTful.properties;

public class ValidateCodeProperties {

    //图片验证码
private ImageCodeProperties image; public ImageCodeProperties getImage() {
return image;
} public void setImage(ImageCodeProperties image) {
this.image = image;
} }

ValidateCodeProperties.java

  优化:配置Filter哪些请求需要拦截器执行

  ImageCodeProperties.java中添加一个String类型的url

  application.properties中添加一个gary.security.code.image.url,用来配置哪些需要我们验证码的Filter

  在ValidateCodeFilter.java将用户请求的url进行切割保存到Set集合当中,遍历Set集合看是否有请求与我们request中的url一致

//在garySecurityProperties.code.image.url    /user,/user/*
//当Bean组装好之后回调用这个函数
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet(); //切割用户配置的url
String[] configUrls = StringUtils.split(garySecurityProperties.getCode().getImage().getUrl(), ","); //将数组放入urls中
for(String configURL : configUrls)
{
urls.add(configURL);
}
//loginPage一定会用到这个Filter,所以我们必须加上
urls.add("/loginPage"); }

  doFilterInternal()通过循环判断是否有匹配的路径

//Filter执行
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { //loginPage
//if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post")) //判断是否有匹配的路径
boolean action = false; //循环判断
for(String url :urls)
{
if(antPathMatcher.match(url, request.getRequestURI()))
{
action = true;
}
} if(action)
{
//filter才会执行
try
{
validate(new ServletWebRequest(request));
}
catch(ValidateCodeException e) {
//判处验证码的异常
authenticationFailureHandler.onAuthenticationFailure(request,response,e);
//一旦出现异常,我们就不就不能继续执行(应该放行),应该return
return;
} } //放行
filterChain.doFilter(request, response); }

#datasource
spring.datasource.url=jdbc:mysql:///springsecurity?serverTimezone=UTC&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.dricer-class-name=com.mysql.jdbc.Driver #jpa
#打印出数据库语句
spring.jpa.show-sql=true
#更新数据库表
spring.jpa.hibernate.ddl-auto=update #配置登陆方式
gary.security.loginType = JSON server.port=8081 #验证码长度
gary.security.code.image.length = 6
#验证码图片的长
gary.security.code.image.width = 100 #配置哪些需要我们验证码的Filter
gary.security.code.image.url = /user,/user/*

application.properties

package com.Gary.GaryRESTful.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.Gary.GaryRESTful.filter.ValidateCodeFilter;
import com.Gary.GaryRESTful.handler.LoginFailureHandler;
import com.Gary.GaryRESTful.handler.LoginSuccessHandler;
import com.Gary.GaryRESTful.properties.GarySecurityProperties; //Web应用安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{ //告诉SpringSecurity密码用什么加密的
@Bean
public PasswordEncoder passwordEncoder()
{
return new BCryptPasswordEncoder();
} @Autowired
private LoginSuccessHandler loginSuccessHandler; @Autowired
private LoginFailureHandler loginFailureHandler; @Autowired
private GarySecurityProperties garySecurityProperties; protected void configure(HttpSecurity http) throws Exception{ //声明我们自己写的过滤器
ValidateCodeFilter validateCodeFilter = new ValidateCodeFilter();
//给过滤器赋值
validateCodeFilter.setAuthenticationFailureHandler(loginFailureHandler);
validateCodeFilter.setGarySecurityProperties(garySecurityProperties);
validateCodeFilter.afterPropertiesSet(); //表单验证(身份认证)
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class) .formLogin()
//自定义登陆页面
.loginPage("/require")
//如果URL为loginPage,则用SpringSecurity中自带的过滤器去处理该请求
.loginProcessingUrl("/loginPage")
//配置登陆成功调用loginSuccessHandler
.successHandler(loginSuccessHandler)
//配置登陆失败调用loginFailureHandler
.failureHandler(loginFailureHandler)
.and()
//请求授权
.authorizeRequests()
//在访问我们的URL时,我们是不需要省份认证,可以立即访问
.antMatchers("/login.html","/require","/code/image").permitAll()
//所有请求都被拦截,跳转到(/login请求中)
.anyRequest()
//都需要我们身份认证
.authenticated()
//SpringSecurity保护机制
.and().csrf().disable();
} }

SecurityConfig.java

package com.Gary.GaryRESTful.filter;

import java.io.IOException;
import java.util.HashSet;
import java.util.Set; import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.filter.OncePerRequestFilter; import com.Gary.GaryRESTful.controller.ValidateCodeController;
import com.Gary.GaryRESTful.exception.ValidateCodeException;
import com.Gary.GaryRESTful.properties.GarySecurityProperties;
import com.Gary.GaryRESTful.validate.code.ImageCode; //继承OncePerRequestFilter,保证这个filter只会执行一次
public class ValidateCodeFilter extends OncePerRequestFilter implements InitializingBean{ private AuthenticationFailureHandler authenticationFailureHandler; //操作session域的工具
private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy(); private GarySecurityProperties garySecurityProperties; private Set<String> urls = new HashSet<String>(); //为了处理/user/*的形式
private AntPathMatcher antPathMatcher = new AntPathMatcher(); //在garySecurityProperties.code.image.url /user,/user/*
//当Bean组装好之后回调用这个函数
@Override
public void afterPropertiesSet() throws ServletException {
super.afterPropertiesSet(); //切割用户配置的url
String[] configUrls = StringUtils.split(garySecurityProperties.getCode().getImage().getUrl(), ","); //将数组放入urls中
for(String configURL : configUrls)
{
urls.add(configURL);
}
//loginPage一定会用到这个Filter,所以我们必须加上
urls.add("/loginPage"); } public GarySecurityProperties getGarySecurityProperties() {
return garySecurityProperties;
} public void setGarySecurityProperties(GarySecurityProperties garySecurityProperties) {
this.garySecurityProperties = garySecurityProperties;
} //Filter执行
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException { //loginPage
//if(StringUtils.pathEquals("/loginPage", request.getRequestURI()) && StringUtils.startsWithIgnoreCase(request.getMethod(), "post")) //判断是否有匹配的路径
boolean action = false; //循环判断
for(String url :urls)
{
if(antPathMatcher.match(url, request.getRequestURI()))
{
action = true;
}
} if(action)
{
//filter才会执行
try
{
validate(new ServletWebRequest(request));
}
catch(ValidateCodeException e) {
//判处验证码的异常
authenticationFailureHandler.onAuthenticationFailure(request,response,e);
//一旦出现异常,我们就不就不能继续执行(应该放行),应该return
return;
} } //放行
filterChain.doFilter(request, response); } //校验验证码是否正确
private void validate(ServletWebRequest request) throws ServletRequestBindingException { // 获得session域中正确的验证码
ImageCode codeInSession = (ImageCode) sessionStrategy.getAttribute(request, ValidateCodeController.sessionKey);
// 获得request域中的用户输入的验证码imageCode
String codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), "imageCode"); // 判断用户输入的验证码是否为空
if(StringUtils.isEmpty(codeInRequest))
{
throw new ValidateCodeException("验证码不能为空");
} // 判断session域中的验证码是否为null
if(codeInSession == null)
{
throw new ValidateCodeException("验证码不存在");
} // 判断验证码是否过期
if(codeInSession.isExpried())
{
throw new ValidateCodeException("验证码已过期");
} // 校验两个验证码是否匹配
if(!StringUtils.pathEquals(codeInSession.getCode(), codeInRequest))
{
//System.out.println("正确的:"+codeInSession.getCode());
//System.out.println("用户输入的:"+codeInRequest);
throw new ValidateCodeException("验证码不匹配");
} // 将验证码从session域中移除
sessionStrategy.removeAttribute(request, ValidateCodeController.sessionKey); } public AuthenticationFailureHandler getAuthenticationFailureHandler() {
return authenticationFailureHandler;
} public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) {
this.authenticationFailureHandler = authenticationFailureHandler;
} public SessionStrategy getSessionStrategy() {
return sessionStrategy;
} public void setSessionStrategy(SessionStrategy sessionStrategy) {
this.sessionStrategy = sessionStrategy;
} }

ValidateCodeFilter.java

JavaWeb-SpringSecurity图片验证ImageCode的更多相关文章

  1. $Django 图片验证刷新 上传头像

    1.图片验证刷新 $('img').click(function () { $('img')[0].src+='?' }) 2.上传头像 1.模板 <div class="form-g ...

  2. $Django 表设计,登陆图片验证

    pip3 install pillow #PIL登陆图片验证(未实现局部刷新)详细:https://www.cnblogs.com/qiangyuge/p/8025168.htmldef get_co ...

  3. 滑动验证 和滑动图片验证JS

    滑动验证 先放效果图 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&quo ...

  4. QTP图片验证/图片比较/二进制流对比法

    图片验证主要是文件对比,其中我们可以利用二进制的方法读取图片信息,然后进行对比,达到对比的效果,本例子利用fso对象的文件流的方法实现,代码如下: Public Function CompareFil ...

  5. thinkphp5.0 实现图片验证效果且能点击图片刷新图片

    思路与文件上传相同,只是验证码一个方法: <img src="{:captcha_src()}" /> 后台文件:app\ceshi\yam <?php name ...

  6. 完整说明使用SpringBoot+js实现滑动图片验证

    常见的网站验证方式有手机短信验证,图片字符验证,滑块验证,滑块图片验证.本文主要讲解的是滑块图片验证的实现流程.包括后台和前端的实现. 实现效果 使用的API java.awt.image.Buffe ...

  7. JAVA实现图片验证

    一.什么是图片验证码? 可以参考下面这张图: 我们在一些网站登陆的时候,经常需要填写以上图片的信息. 这种图片验证方式是我们最常见的形式,它可以有效的防范恶意攻击者采用恶意工具,来进行窃取用户的密码 ...

  8. c# 简单的滑动图片验证

    普通的验证码对用户使用体验不友好,出现了滑动图片验证的验证方式,用户只要按住滑块完成图片的拼接即可通过验证(这是最简单的方式,滑动轨迹,数据分析,滑行速度 什么的暂没考虑) 主要的实现思路: 1.先从 ...

  9. javaWeb——图片验证

    publicvoid doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, ...

随机推荐

  1. 面试常考的js题目(三)

    1.查找两个节点的最近的一个共同父节点,可以包括节点自身 function commonParentNode(oNode1, oNode2) { if(oNode1.contains(oNode2)) ...

  2. 学习笔记--三分法&秦九韶算法

    前言 其实也没什么好说的吧,三分法就是用来求一个单调函数的最值和满足最大值的\(x\),秦九韶算法就是在\(O(N)\)时间内求一个多项式值 怎么用 三分法使用--看这篇:https://www.cn ...

  3. JS ES5

    常用 严格模式 use strict 必须使用var声明变量 禁止自定义函数this指向window 'use strict' funcion Person(name){ this.name = na ...

  4. 【Git的基本操作六】分支管理

    分支管理 1. 什么是分支? 在版本控制过程中,使用对条线同时推进多个任务. 2. 分支的好处 同时并行推进多个功能开发,提高开发效率 各个分支在开发过程中,如果某一个分支开发失败,不会对其他分支有任 ...

  5. http://www.moext.com博客搬家到这里啦

    1.原博客莫叉特用的是自己的域名http://www.moext.com,由于服务器在国外,访问不太稳定,SEO做得也很不好: 2.喜欢博客园的极简风格,目前来看广告量也在可接受范围: 3.一个偶然的 ...

  6. Nginx作为代理服务之正反代理

    Nginx作为代理服务之正反代理 首先什么是代理,就跟明星的经纪人类似,比如作为苍老师经纪人的我,如果你们需要和苍老师拍小电影,可以跟我这个经纪人来商量比如价格啊,时间等相关信息,那么我就作为一个代理 ...

  7. Oracle 11.2.0.1 ADG环境MRP进程遭遇ORA

    环境:Linux + Oracle 11.2.0.1 ADG现象:发现备库没有应用日志 1. 数据库查询备库目前状态发现备库目前没有应用日志,apply lag已经显示备库有3天21小时多没有应用日志 ...

  8. ASE19 团队项目 模型组 scrum report集合

    scrum report 链接 scrum1 report scrum2 report scrum3 report scrum4 report scrum5 report scrum6 report ...

  9. fetch的文件流下载及下载进度获取

    下载过程中,获取进度,fetch API并没有提供类似xhr和ajax的 progress所以用 getReader()来循环读取大小 let size = 0; fetch( URL() + `/s ...

  10. (转)Java new一个对象的过程中发生了什么

    Java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名(包名+类名)来加载.加载并初始化类完成后,再进行对象的创建工作. 我们先假设是第一次使用该 ...