转载自 : www.javaman.cn
1、编写工具类生成4位随机数
该工具类主要生成从0-9,a-z,A-Z范围内产生的4位随机数
/*** 产生4位随机字符串*/public static String getCheckCode() {String base = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";int size = base.length();Random r = new Random();StringBuffer sb = new StringBuffer();for (int i = 1; i <= 4; i++) {//产生0到size-1的随机值int index = r.nextInt(size);//在base字符串中获取下标为index的字符char c = base.charAt(index);//将c放入到StringBuffer中去sb.append(c);}return sb.toString();}
2、编写常量类
用户常量的绑定,所有的常量都可以在ConfigConsts中定义,方便管理。
import java.util.Arrays;
import java.util.List;public interface ConfigConsts {/*** 验证码存session*/String IMAGE_CODE_SESSION = "IMAGE_CODE";
}
3、获取验证码接口
这段代码的主要作用是为用户生成一个图片验证码,并将其显示在浏览器中。当调用该代码对应的URL时,服务器会创建一个包含随机验证码的图片,并将此验证码存储在用户的会话中,然后将该图片发送给用户的浏览器显示
/*** 验证码*/@RequestMapping("/getImgCode")public void getImgCode(HttpServletRequest request, HttpServletResponse response) {int width = 80;int height = 30;BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);//获取画笔Graphics graphics = image.getGraphics();//设置画笔颜色为白色graphics.setColor(Color.white);//填充图片graphics.fillRect(0, 0, width, height);//设置画笔颜色为黑色graphics.setColor(Color.black);//设置字体的小大graphics.setFont(new Font("黑体", Font.BOLD, 24));//产生4个随机验证码String checkCode = CommonUtil.getCheckCode();//将验证码放入HttpSession中HttpSession session = request.getSession();session.setAttribute(ConfigConsts.IMAGE_CODE_SESSION, checkCode);//向图片上写入验证码graphics.drawString(checkCode, 15, 25);//将内存中的图片输出到浏览器try {response.setContentType("image/png");ImageIO.write(image, "PNG", response.getOutputStream());} catch (IOException e) {e.printStackTrace();}}
4、controller跳转到登录页
对“/loginPage”的GET请求,并将用户重定向到登录页面。当调用这个URL时,系统会返回一个名为"login"的视图(通常是一个HTML页面),这个视图通常用于显示登录表单,让用户输入用户名和密码等信息。
/*** 跳转到登陆页面* @return 登陆页面*/@GetMapping("/loginPage")public String loginPage(){return "login";}
5、登录界面
在Web页面上实现一个图形验证码的输入功能
- HTML部分:
- 创建一个表单项,内部包含两列(使用layui的栅格系统)。
- 在第一列中,有一个标签和一个文本输入框。标签用于显示一个验证码图标,输入框用于用户输入图形验证码。
- 在第二列中,有一个图片元素用于显示图形验证码图片。
- JavaScript部分:
- 配置layui的静态资源路径和主入口模块。
- 初始化时,调用
getImgCode
函数加载验证码图片,并渲染表单。 getImgCode
函数通过Ajax请求从服务器获取验证码图片,并将其显示在页面上的图片元素中。
这段代码主要利用了layui框架来实现页面的布局和交互,同时通过JavaScript和Ajax实现与服务器的通信,以获取并显示图形验证码。
<div class="layui-form-item"><div class="layui-row"><div class="layui-col-xs7"><label class="layadmin-user-login-icon layui-icon layui-icon-vercode"></label><input type="text" name="code" lay-verify="required" placeholder="图形验证码" class="layui-input"></div><div class="layui-col-xs5"><div style="margin-left: 10px;"><img id="codeImg" class="layadmin-user-login-codeimg"></div></div></div><script>layui.config({base: '/static/layuiadmin/' //静态资源所在路径}).extend({index: 'lib/index' //主入口模块}).use(['index', 'user'], function(){let $ = layui.$,form = layui.form;// 初始化getImgCode();form.render();}/*** 获取验证码*/function getImgCode() {let url = ctx + '/getImgCode';let xhr = new XMLHttpRequest();xhr.open('GET', url, true);xhr.responseType = "blob";xhr.onload = function() {if (this.status === 200) {let blob = this.response;document.getElementById("codeImg").src = window.URL.createObjectURL(blob);}}xhr.send();}</script>
6、验证码过滤
校验验证码的过滤器,基于Java的Spring框架。
- 该过滤器继承了
OncePerRequestFilter
,确保每次请求只被过滤一次。 - 在
doFilterInternal
方法中,它首先检查请求是否是登录请求(通过检查请求路径是否为"/login"以及请求方法是否为"POST")。 - 如果是登录请求,它会调用
validate
方法来校验验证码。 validate
方法从请求中获取验证码,然后与会话中存储的验证码进行比对。如果验证码不存在、为空或不匹配,将抛出异常。- 如果验证码校验失败,过滤器会捕获异常,并向响应中写入错误信息(以JSON格式)。
- 如果请求不是登录请求,过滤器不会进行验证码校验,直接让请求继续向下执行(通过调用
filterChain.doFilter
)。 - 如果登录请求成功通过验证码校验,代码会继续执行后续的过滤器或处理器。
@Component
public class ValidateCodeFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {// 登陆请求if ("/login".equals(httpServletRequest.getServletPath()) &&"POST".equalsIgnoreCase(httpServletRequest.getMethod())){try {validate(httpServletRequest);} catch (Exception exception) {httpServletResponse.setCharacterEncoding("utf-8");httpServletResponse.setContentType("application/json;charset=UTF-8");PrintWriter writer = httpServletResponse.getWriter();writer.write(JSON.toJSONString(Result.failure(exception.getMessage())));writer.flush();return;}}// 不是一个登录请求,不做校验 直接通过filterChain.doFilter(httpServletRequest, httpServletResponse);}private void validate(HttpServletRequest request) {String code = request.getParameter("code");if (StringUtils.isBlank(code)){throw new RuntimeException("验证码不能为空");}Object checkCode = request.getSession(false).getAttribute(ConfigConsts.IMAGE_CODE_SESSION);if (Objects.isNull(checkCode)) {throw new RuntimeException("验证码不存在");}if (!StringUtils.equalsIgnoreCase(code,checkCode.toString())) {throw new RuntimeException("验证码不匹配");}request.getSession(false).removeAttribute(ConfigConsts.IMAGE_CODE_SESSION);}
}
7、集成mysecurity
集成Spring Security的安全配置类,用于Web应用的安全性设置。
- 通过
@EnableWebSecurity
和@Configuration
注解,启用并配置Spring Security。 - 使用
@EnableGlobalMethodSecurity(prePostEnabled = true)
来启用全局方法级别的安全性,允许使用例如@PreAuthorize
和@PostAuthorize
等注解。 - 定义了一个名为
MySecurityConfig
的配置类,该类继承自WebSecurityConfigurerAdapter
,用于定制安全性设置。 - 通过
@Autowired
注入了一个名为validateCodeFilter
的验证码过滤器实例。 - 在
configure(HttpSecurity http)
方法中,对应用的安全性进行了详细配置:- 允许所有人访问
/loginPage
和/getImgCode
这两个路径,不进行任何安全检查。 - 对所有其他请求,需要用户进行身份验证(即需要登录后才能访问)。
- 在用户名和密码验证过滤器之前,添加了一个自定义的验证码过滤器(
validateCodeFilter
),用于在登录过程中校验验证码。
- 允许所有人访问
这段代码的主要目的是增强Web应用的安全性,限制了只有经过身份验证的用户才能访问应用的受保护资源。
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate ValidateCodeFilter validateCodeFilter;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests()// 放过.antMatchers("/loginPage", "/getImgCode").permitAll().anyRequest().authenticated().and()// 过滤登录验证码.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)}
运行结果如下: