最终实现效果---
杂七杂八做了不少小项目,感觉过段时间就忘,这直接就记录下来吧!
前端什么的也只能基本看懂,注重后端开发就行
(css,js静态资源放网盘了)
链接:https://pan.baidu.com/s/1mOKQ9mKKJEkZn9dvmZ0GCQ
提取码:urhv
首先页面构建

1 <!DOCTYPE html> 2 <html lang="en" xmlns:th="http://www.thymeleaf.org"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 5 <title>SSM框架后台管理员登录</title> 6 <meta name="description" content="particles.js is a lightweight JavaScript library for creating particles."> 7 <meta name="author" content="Vincent Garreau"> 8 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 9 <link rel="stylesheet" media="screen" th:href="@{/xadmin/login/css/style.css}"> 10 <link rel="stylesheet" type="text/css" th:href="@{/xadmin/login/css/reset.css}"> 11 <body> 12 13 <div id="particles-js"> 14 <div class="login" style="display: block;"> 15 <div class="login-top"> 16 登录 17 </div> 18 <div class="login-center clearfix"> 19 <div class="login-center-img"><img th:src="@{/xadmin/login/images/name.png}"></div> 20 <div class="login-center-input"> 21 <input type="text" name="userName" id="username" value="" placeholder="请输入您的用户名" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的用户名'"> 22 <div class="login-center-input-text">用户名</div> 23 </div> 24 </div> 25 <div class="login-center clearfix"> 26 <div class="login-center-img"><img th:src="@{/xadmin/login/images/password.png}"></div> 27 <div class="login-center-input"> 28 <input type="password" name="passWord" id="password" value="" placeholder="请输入您的密码" onfocus="this.placeholder=''" onblur="this.placeholder='请输入您的密码'"> 29 <div class="login-center-input-text">密码</div> 30 </div> 31 </div> 32 <div class="login-center clearfix"> 33 <div class="login-center-img"><img th:src="@{/xadmin/login/images/cpacha.png}"></div> 34 <div class="login-center-input"> 35 <input style="width:50%;" type="text" name="cpacha" id="cpacha" value="" placeholder="请输入验证码" onfocus="this.placeholder=''" onblur="this.placeholder='请输入验证码'"> 36 <div class="login-center-input-text">验证码</div> 37 <img id="cpacha-img" title="点击切换验证码" style="cursor:pointer;" src="get_cpacha?vl=4&w=150&h=40&type=loginCpacha" width="110px" height="30px" onclick="changeCpacha()"> 38 </div> 39 </div> 40 <div class="login-button"> 41 登录 42 </div> 43 </div> 44 <div class="sk-rotating-plane"></div> 45 <canvas class="particles-js-canvas-el" width="1147" height="952" style="width: 100%; height: 100%;"></canvas></div> 46 47 <!-- scripts --> 48 <script th:src="@{/xadmin/login/js/particles.min.js}"></script> 49 <script th:src="@{/xadmin/login/js/app.js}"></script> 50 <script th:src="@{/xadmin/login/js/jquery-1.8.0.min.js}"></script> 51 <script type="text/javascript"> 52 function hasClass(elem, cls) { 53 cls = cls || ''; 54 if (cls.replace(/\s/g, '').length == 0) return false; //当cls没有参数时,返回false 55 return new RegExp(' ' + cls + ' ').test(' ' + elem.className + ' '); 56 } 57 58 function addClass(ele, cls) { 59 if (!hasClass(ele, cls)) { 60 ele.className = ele.className == '' ? cls : ele.className + ' ' + cls; 61 } 62 } 63 64 function removeClass(ele, cls) { 65 if (hasClass(ele, cls)) { 66 var newClass = ' ' + ele.className.replace(/[\t\r\n]/g, '') + ' '; 67 while (newClass.indexOf(' ' + cls + ' ') >= 0) { 68 newClass = newClass.replace(' ' + cls + ' ', ' '); 69 } 70 ele.className = newClass.replace(/^\s+|\s+$/g, ''); 71 } 72 } 73 74 function changeCpacha(){ 75 $("#cpacha-img").attr("src",'get_cpacha?vl=4&w=150&h=40&type=loginCpacha&t=' + new Date().getTime()); 76 } 77 document.querySelector(".login-button").onclick = function(){ 78 var userName = $("#username").val(); 79 var passWord = $("#password").val(); 80 var cpacha = $("#cpacha").val(); 81 if(userName == '' || userName == 'undefined'){ 82 alert("请填写用户名!"); 83 return; 84 } 85 if(passWord == '' || passWord == 'undefined'){ 86 alert("请填写密码!"); 87 return; 88 } 89 if(cpacha == '' || cpacha == 'undefined'){ 90 alert("请填写验证码!"); 91 return; 92 } 93 addClass(document.querySelector(".login"), "active") 94 addClass(document.querySelector(".sk-rotating-plane"), "active") 95 document.querySelector(".login").style.display = "none" 96 $.ajax({ 97 url:'/login', 98 data:{userName:userName,passWord:passWord,cpacha:cpacha}, 99 type:'post', 100 dataType:'json', 101 success:function(results){ 102 if(results.code == 200){ 103 window.parent.location = '/index'; 104 //alert("登录成功!"); 105 }else{ 106 removeClass(document.querySelector(".login"), "active"); 107 removeClass(document.querySelector(".sk-rotating-plane"), "active"); 108 document.querySelector(".login").style.display = "block"; 109 alert(results.msg); 110 changeCpacha(); 111 } 112 } 113 }); 114 115 } 116 </script> 117 </body></html>
验证码--随机数字输入验证,原理:向服务端请求,生成随机的字符,写入会话请求,同时将随机字符生成对应图片,响应给前端;前端输入对应字符的验证码,向后台发起校验。
这种没必要死记,网上这种一搜大把,只要理解其实现原理就行,不会就直接复制粘贴

1 package com.beilin.util; 2 3 import java.awt.Color; 4 import java.awt.Font; 5 import java.awt.Graphics; 6 import java.awt.Graphics2D; 7 import java.awt.image.BufferedImage; 8 import java.util.Random; 9 10 /** 11 * 验证码生成器 12 * 13 * @author llq 14 */ 15 public class CpachaUtil { 16 17 /** 18 * 验证码来源 19 */ 20 final private char[] code = { 21 '2', '3', '4', '5', '6', '7', '8', '9', 22 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 23 'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 24 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 25 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 26 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' 27 }; 28 /** 29 * 字体 30 */ 31 final private String[] fontNames = new String[]{ 32 "黑体", "宋体", "Courier", "Arial", 33 "Verdana", "Times", "Tahoma", "Georgia"}; 34 /** 35 * 字体样式 36 */ 37 final private int[] fontStyles = new int[]{ 38 Font.BOLD, Font.ITALIC|Font.BOLD 39 }; 40 41 /** 42 * 验证码长度 43 * 默认4个字符 44 */ 45 private int vcodeLen = 4; 46 /** 47 * 验证码图片字体大小 48 * 默认17 49 */ 50 private int fontsize = 25; 51 /** 52 * 验证码图片宽度 53 */ 54 private int width = (fontsize+1)*vcodeLen+10; 55 /** 56 * 验证码图片高度 57 */ 58 private int height = fontsize+12; 59 /** 60 * 干扰线条数 61 * 默认3条 62 */ 63 private int disturbline = 3; 64 65 66 public CpachaUtil(){} 67 68 /** 69 * 指定验证码长度 70 * @param vcodeLen 验证码长度 71 */ 72 public CpachaUtil(int vcodeLen) { 73 this.vcodeLen = vcodeLen; 74 this.width = (fontsize+1)*vcodeLen+10; 75 } 76 77 /** 78 * 指定验证码长度、图片宽度、高度 79 * @param vcodeLen 80 * @param width 81 * @param height 82 */ 83 public CpachaUtil(int vcodeLen,int width,int height) { 84 this.vcodeLen = vcodeLen; 85 this.width = width; 86 this.height = height; 87 } 88 89 /** 90 * 生成验证码图片 91 * @param vcode 要画的验证码 92 * @param drawline 是否画干扰线 93 * @return 94 */ 95 public BufferedImage generatorVCodeImage(String vcode, boolean drawline){ 96 //创建验证码图片 97 BufferedImage vcodeImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 98 Graphics g = vcodeImage.getGraphics(); 99 //填充背景色 100 g.setColor(new Color(246, 240, 250)); 101 g.fillRect(0, 0, width, height); 102 if(drawline){ 103 drawDisturbLine(g); 104 } 105 //用于生成伪随机数 106 Random ran = new Random(); 107 //在图片上画验证码 108 for(int i = 0;i < vcode.length();i++){ 109 //设置字体 110 g.setFont(new Font(fontNames[ran.nextInt(fontNames.length)], fontStyles[ran.nextInt(fontStyles.length)], fontsize)); 111 //随机生成颜色 112 g.setColor(getRandomColor()); 113 //画验证码 114 g.drawString(vcode.charAt(i)+"", i*fontsize+10, fontsize+5); 115 } 116 //释放此图形的上下文以及它使用的所有系统资源 117 g.dispose(); 118 119 return vcodeImage; 120 } 121 /** 122 * 获得旋转字体的验证码图片 123 * @param vcode 124 * @param drawline 是否画干扰线 125 * @return 126 */ 127 public BufferedImage generatorRotateVCodeImage(String vcode, boolean drawline){ 128 //创建验证码图片 129 BufferedImage rotateVcodeImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); 130 Graphics2D g2d = rotateVcodeImage.createGraphics(); 131 //填充背景色 132 g2d.setColor(new Color(246, 240, 250)); 133 g2d.fillRect(0, 0, width, height); 134 if(drawline){ 135 drawDisturbLine(g2d); 136 } 137 //在图片上画验证码 138 for(int i = 0;i < vcode.length();i++){ 139 BufferedImage rotateImage = getRotateImage(vcode.charAt(i)); 140 g2d.drawImage(rotateImage, null, (int) (this.height * 0.7) * i, 0); 141 } 142 g2d.dispose(); 143 return rotateVcodeImage; 144 } 145 /** 146 * 生成验证码 147 * @return 验证码 148 */ 149 public String generatorVCode(){ 150 int len = code.length; 151 Random ran = new Random(); 152 StringBuffer sb = new StringBuffer(); 153 for(int i = 0;i < vcodeLen;i++){ 154 int index = ran.nextInt(len); 155 sb.append(code[index]); 156 } 157 return sb.toString(); 158 } 159 /** 160 * 为验证码图片画一些干扰线 161 * @param g 162 */ 163 private void drawDisturbLine(Graphics g){ 164 Random ran = new Random(); 165 for(int i = 0;i < disturbline;i++){ 166 int x1 = ran.nextInt(width); 167 int y1 = ran.nextInt(height); 168 int x2 = ran.nextInt(width); 169 int y2 = ran.nextInt(height); 170 g.setColor(getRandomColor()); 171 //画干扰线 172 g.drawLine(x1, y1, x2, y2); 173 } 174 } 175 /** 176 * 获取一张旋转的图片 177 * @param c 要画的字符 178 * @return 179 */ 180 private BufferedImage getRotateImage(char c){ 181 BufferedImage rotateImage = new BufferedImage(height, height, BufferedImage.TYPE_INT_ARGB); 182 Graphics2D g2d = rotateImage.createGraphics(); 183 //设置透明度为0 184 g2d.setColor(new Color(255, 255, 255, 0)); 185 g2d.fillRect(0, 0, height, height); 186 Random ran = new Random(); 187 g2d.setFont(new Font(fontNames[ran.nextInt(fontNames.length)], fontStyles[ran.nextInt(fontStyles.length)], fontsize)); 188 g2d.setColor(getRandomColor()); 189 double theta = getTheta(); 190 //旋转图片 191 g2d.rotate(theta, height/2, height/2); 192 g2d.drawString(Character.toString(c), (height-fontsize)/2, fontsize+5); 193 g2d.dispose(); 194 195 return rotateImage; 196 } 197 /** 198 * @return 返回一个随机颜色 199 */ 200 private Color getRandomColor(){ 201 Random ran = new Random(); 202 return new Color(ran.nextInt(220), ran.nextInt(220), ran.nextInt(220)); 203 } 204 /** 205 * @return 角度 206 */ 207 private double getTheta(){ 208 return ((int) (Math.random()*1000) % 2 == 0 ? -1 : 1)*Math.random(); 209 } 210 211 /** 212 * @return 验证码字符个数 213 */ 214 public int getVcodeLen() { 215 return vcodeLen; 216 } 217 /** 218 * 设置验证码字符个数 219 * @param vcodeLen 220 */ 221 public void setVcodeLen(int vcodeLen) { 222 this.width = (fontsize+3)*vcodeLen+10; 223 this.vcodeLen = vcodeLen; 224 } 225 /** 226 * @return 字体大小 227 */ 228 public int getFontsize() { 229 return fontsize; 230 } 231 /** 232 * 设置字体大小 233 * @param fontsize 234 */ 235 public void setFontsize(int fontsize) { 236 this.width = (fontsize+3)*vcodeLen+10; 237 this.height = fontsize+15; 238 this.fontsize = fontsize; 239 } 240 /** 241 * @return 图片宽度 242 */ 243 public int getWidth() { 244 return width; 245 } 246 /** 247 * 设置图片宽度 248 * @param width 249 */ 250 public void setWidth(int width) { 251 this.width = width; 252 } 253 /** 254 * @return 图片高度 255 */ 256 public int getHeight() { 257 return height; 258 } 259 /** 260 * 设置图片高度 261 * @param height 262 */ 263 public void setHeight(int height) { 264 this.height = height; 265 } 266 /** 267 * @return 干扰线条数 268 */ 269 public int getDisturbline() { 270 return disturbline; 271 } 272 /** 273 * 设置干扰线条数 274 * @param disturbline 275 */ 276 public void setDisturbline(int disturbline) { 277 this.disturbline = disturbline; 278 } 279 280 }
(与登录相关的操作都放在LoginController,里面都有中文注解,很多东西只可意会不可言传)

1 package com.beilin.controller; 2 3 4 import com.beilin.Service.UserService; 5 import com.beilin.entity.SysUser; 6 import com.beilin.result.ResponseCode; 7 import com.beilin.result.Results; 8 import com.beilin.util.CpachaUtil; 9 import com.beilin.util.Md5Cipher; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.stereotype.Controller; 12 import org.springframework.web.bind.annotation.*; 13 import org.springframework.web.servlet.ModelAndView; 14 15 import javax.imageio.ImageIO; 16 import javax.servlet.http.HttpServletRequest; 17 import javax.servlet.http.HttpServletResponse; 18 import java.awt.image.BufferedImage; 19 import java.io.IOException; 20 21 /** 22 * 系统登录控制器相关 23 */ 24 @Controller 25 public class LoginController { 26 @Autowired 27 private UserService userService; 28 29 30 /** 31 * 登录成功后的主页 32 * @param model 33 * @return 34 */ 35 @GetMapping("/index") 36 public ModelAndView index(ModelAndView model) { 37 model.setViewName("index"); 38 return model; 39 } 40 41 /** 42 * 打开登录页面 43 * @param model 44 * @return 45 */ 46 @GetMapping("/login") 47 public ModelAndView login(ModelAndView model) { 48 model.setViewName("login"); 49 return model; 50 } 51 52 53 /** 54 * 本系统所有的验证码均采用此方法 55 * @param vcodeLen 56 * @param width 57 * @param height 58 * @param cpachaType:用来区别验证码的类型,传入字符串 59 * @param request 60 * @param response 61 */ 62 @GetMapping (value="/get_cpacha") 63 public void generateCpacha( 64 @RequestParam(name="vl",required=false,defaultValue="4") Integer vcodeLen, 65 @RequestParam(name="w",required=false,defaultValue="100") Integer width, 66 @RequestParam(name="h",required=false,defaultValue="30") Integer height, 67 @RequestParam(name="type",required=true,defaultValue="loginCpacha") String cpachaType, 68 HttpServletRequest request, 69 HttpServletResponse response){ 70 //调用CpachaUtil中的构造方法指定验证码的长度,宽度,高度 71 CpachaUtil cpachaUtil = new CpachaUtil(vcodeLen, width, height); 72 //生成验证码 73 String generatorVCode = cpachaUtil.generatorVCode(); 74 request.getSession().setAttribute(cpachaType, generatorVCode); 75 //把随机取到的验证码传入图片 true--需要干扰线 76 BufferedImage generatorRotateVCodeImage = cpachaUtil.generatorRotateVCodeImage(generatorVCode, true); 77 try { 78 ImageIO.write(generatorRotateVCodeImage, "gif", response.getOutputStream()); 79 } catch (IOException e) { 80 // TODO Auto-generated catch block 81 e.printStackTrace(); 82 } 83 } 84 85 86 }
配置没问题就实现验证码功能了