总结自开源项目bootdo
图标设置
验证码
验证码生成工具类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;/*** @author bootdo*/
public class RandomValidateCodeUtil {public static final String RANDOMCODEKEY = "RANDOMVALIDATECODEKEY";//放到session中的keyprivate String randString = "0123456789";//随机产生只有数字的字符串 private String//private String randString = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生只有字母的字符串//private String randString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//随机产生数字与字母组合的字符串private int width = 95;// 图片宽private int height = 25;// 图片高private int lineSize = 40;// 干扰线数量private int stringNum = 4;// 随机产生字符数量private static final Logger logger = LoggerFactory.getLogger(RandomValidateCodeUtil.class);private Random random = new Random();/*** 获得字体*/private Font getFont() {return new Font("Fixedsys", Font.CENTER_BASELINE, 18);}/*** 获得颜色*/private Color getRandColor(int fc, int bc) {if (fc > 255) {fc = 255;}if (bc > 255) {bc = 255;}int r = fc + random.nextInt(bc - fc - 16);int g = fc + random.nextInt(bc - fc - 14);int b = fc + random.nextInt(bc - fc - 18);return new Color(r, g, b);}/*** 生成随机图片*/public void getRandcode(HttpServletRequest request, HttpServletResponse response) {HttpSession session = request.getSession();// BufferedImage类是具有缓冲区的Image类,Image类是用于描述图像信息的类BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);Graphics g = image.getGraphics();// 产生Image对象的Graphics对象,改对象可以在图像上进行各种绘制操作g.fillRect(0, 0, width, height);//图片大小g.setFont(new Font("Default", Font.ROMAN_BASELINE, 18));//字体大小g.setColor(getRandColor(110, 133));//字体颜色// 绘制干扰线for (int i = 0; i <= lineSize; i++) {drowLine(g);}// 绘制随机字符String randomString = "";for (int i = 1; i <= stringNum; i++) {randomString = drowString(g, randomString, i);}logger.info(randomString);//将生成的随机字符串保存到session中session.removeAttribute(RANDOMCODEKEY);session.setAttribute(RANDOMCODEKEY, randomString);g.dispose();try {// 将内存中的图片通过流动形式输出到客户端ImageIO.write(image, "JPEG", response.getOutputStream());} catch (Exception e) {logger.error("将内存中的图片通过流动形式输出到客户端失败>>>> ", e);}}/*** 绘制字符串*/private String drowString(Graphics g, String randomString, int i) {g.setFont(getFont());g.setColor(new Color(random.nextInt(101), random.nextInt(111), random.nextInt(121)));String rand = String.valueOf(getRandomString(random.nextInt(randString.length())));randomString += rand;g.translate(random.nextInt(3), random.nextInt(3));g.drawString(rand, 13 * i, 16);return randomString;}/*** 绘制干扰线*/private void drowLine(Graphics g) {int x = random.nextInt(width);int y = random.nextInt(height);int xl = random.nextInt(13);int yl = random.nextInt(15);g.drawLine(x, y, x + xl, y + yl);}/*** 获取随机的字符*/public String getRandomString(int num) {return String.valueOf(randString.charAt(num));}
}
/*** 生成验证码*/@GetMapping(value = "/getVerify")public void getVerify(HttpServletRequest request, HttpServletResponse response) {try {response.setContentType("image/jpeg");//设置相应类型,告诉浏览器输出的内容为图片response.setHeader("Pragma", "No-cache");//设置响应头信息,告诉浏览器不要缓存此内容response.setHeader("Cache-Control", "no-cache");response.setDateHeader("Expire", 0);RandomValidateCodeUtil randomValidateCode = new RandomValidateCodeUtil();randomValidateCode.getRandcode(request, response);//输出验证码图片方法} catch (Exception e) {logger.error("获取验证码失败>>>> ", e);}}
<div class="col-xs-6 pull_left"><a href="javascript:void(0);" rel="external nofollow" title="点击更换验证码"><img style="margin-top: 12px;" id="imgVerify" src="" alt="更换验证码" height="36" width="100%" onclick="getVerify(this);"></a>
</div>//获取验证码function getVerify(obj) {obj.src = "/getVerify?" + Math.random(); //加上随机数,切换图片,避免服务器缓存,相同路径,不再发送请求console.log(obj)}
关于请求头的设置
图片来自菜鸟教程7.1.2 Android Http请求头与响应头的学习 | 菜鸟教程
表单验证
转自Validate表单验证_RocsLee筱站-CSDN博客_validate
validate属性,jquery的小插件
一、 validate的使用步骤
引入jquery.min.js
引入 jquery.validate.js
页面加载后对表单进行验证 $("#表单id名").validate({})
在validate中的rules中编写验证规则(格式如下)
字段的name属性:“校验器”(tisps:一个输入框只有一个校验器的时候使用)
字段的name属性:{校验器:值,校验器:值}(tips:输入框需要有多个校验器的时候使用)
在validate中的messages中编写提示信息(tips格式与rules相对应)
在validate中的submitHandler中编写验证通过执行的内容
二、实例
<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<script type="text/javascript" src="scripts/common/jquery.min.js"></script>
<script type="text/javascript" src="scripts/common/jquery.validate.js"></script
<script type="text/javascript">$("#zhuce").validate({rules:{regist_username:{required:true/* remote:{type: "post",url: url,data:{userName:function () {return $("regist_username").val();}}}用ajax判断数据库中用户名是否存在*/},regist_password:{required:true,minlength:6},final_password:{equalTo:"#regist_password"}},messages:{regist_username:{required:"用户名不能为空"},regist_password:{required:"密码不能为空",minlength:"密码最小的长度为6"},final_password:{equalTo:"两次密码不一致"}},submitHandler: function () {//验证通过后进行注册register();}})</script>
</head>
<body><form id="zhuce"><dl><dt><div class='header'><h3>注 册</h3></div></dt><dt></dt><dt><div class='letter'><label for="regist_username">用户名: </label><input type="text" name="regist_username" id="regist_username" tabindex='5'/></div></dt><dt><div class='letter'>昵 称: <input type="text" name="nickname" id="nickname" tabindex='6'/></div></dt><dt><div class='letter'><label for="regist_password"> 密 码: </label><input type="password" name="regist_password" id="regist_password" tabindex='7'/></div></dt><dt><div class='password'><label for="final_password"> 确认密码: </label><input type="password" name="final_password" id="final_password" tabindex='8'/></div></dt><dt><div><input type="submit" name="" id="regist_button" value=' 注 册 ' tabindex='9'/><input type="button" name="" id="back" value=' 返 回 ' tabindex='10'/></div></dt></dl></form>
</body>
</html>
实例二
function validateRule() {var icon = "<i class='fa fa-times-circle'></i> ";$("#signupForm").validate({ //jquery插件验证表单内容rules: {username: {required: true},password: {required: true}},messages: {username: {required: icon + "请输入您的用户名",},password: {required: icon + "请输入您的密码",}}})}
详细使用jQuery validator 详解/应用_猿来是你-CSDN博客
获取model中数据
thymeleaf模板引擎为前端获取数据提供了较大的便利,在html标签可以通过th标签加${}表达式访问model中的数据。但是如果不想通过th标签而是简单的访问model对象数据,或者是在js中访问modle中的数据,则需要使用内联的方法。
比如我们可以使用[[$ {} ]]访问对象
<p>Hello, [[${session.user.name}]]!</p>
以上的代码可以取代一般的th标签写法
<p>Hello, <span th:text="${session.user.name}">Sebastian</span>!</p>
Expressions between [[...]]
or [(...)]
are considered inlined expressions in Thymeleaf。即使用此语法被称为“内联”。使用内联的语句的确会更加简洁一点。
若是想在javascript代码块内直接的使用model的对象值,则必须使用内联实现。
<script type="text/javascript" th:inline="javascript">/*<![CDATA[*/var max = /*[[${maxSumOfDateInYear}]]*/ 20;alert(max);/*]]>*/
</script>
其中第2、5行为基于XML的转义写法,第3行把内联访问的语句用注释引起来,且后面跟着一个空格20,是指若不存在此对象,则自动设置默认值为20。
注意以上的javascript内联写法是基于你需要“直接地”使用model对象值的场景。否则还有其它写法,比如简单地声明一个hidden input:
<input type="hidden" id="maxSumOfDateInYear" th:value="${maxSumOfDateInYear}"/>
<script type="text/javascript" th:inline="none">var max = $("#maxSumOfDateInYear").val();var data = [["2012-05-07", 6], ["2012-04-16", 4]];alert(max);alert(data);
</script>
以上第1行声明了一个hidden的maxSumOfDateInYear,然后第3行通过jQuery来间接访问。
注意,若在thymeleaf代码里存在第4行的二维数组字面量的写法,则必须要把javascript代码块设置为inline为none的,否则thymeleaf引擎会把此数组的[[也当成了内联语句处理,从而导致后端报错An error happened during template parsing。要么,你就还是通过hidden间接访问的方式实现。
或者,也可以通过ajax直接获取内容来实现。但这就属与thymeleaf模板引擎计算过程无关了。
thymeleaf的内联th:inline(在javascript访问model中的数据)_纱厨藤簟,玉人罗扇轻缣。-CSDN博客
登录验证
发送请求
function login() {$.ajax({type: "POST",url: ctx + "login",//serialize() 方法通过序列化表单值,创建 URL 编码文本字符串。您可以选择一个或多个表单元素(比如 input 及/或 文本框),// 或者 form 元素本身。序列化的值可在生成 AJAX 请求时用于 URL 查询字符串中。data: $('#signupForm').serialize(),success: function (r) {if (r.code == 0) {var index = layer.load(1, {shade: [0.1, '#fff'] //0.1透明度的白色背景});// 父页面跳转 http://localhost/indexparent.location.href = '/index';} else {//jquery插件 layer弹窗jslayer.msg(r.msg);}},});}
接收请求
@PostMapping("/login")@ResponseBodyR ajaxLogin(String username, String password,String verify,HttpServletRequest request) {try {//从session中获取验证码的值String random = (String) request.getSession().getAttribute(RandomValidateCodeUtil.RANDOMCODEKEY);if (StringUtils.isBlank(verify)) { //isBlank 判空return R.error("请输入验证码");}if (random.equals(verify)) {} else {return R.error("请输入正确的验证码");}} catch (Exception e) {logger.error("验证码校验失败", e);return R.error("验证码校验失败");}// md5加盐算法后的密码password = MD5Utils.encrypt(username, password);UsernamePasswordToken token = new UsernamePasswordToken(username, password);Subject subject = SecurityUtils.getSubject();try {subject.login(token);return R.ok();} catch (AuthenticationException e) {return R.error("用户或密码错误");}}
R是一个标志类
import java.util.HashMap;
import java.util.Map;public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R() {put("code", 0);put("msg", "操作成功");}public static R error() {return error(1, "操作失败");}public static R error(String msg) {return error(500, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}@Overridepublic R put(String key, Object value) {super.put(key, value);return this;}
}
MD5加盐工具类
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;public class MD5Utils {private static final String SALT = "1qazxsw2";private static final String ALGORITH_NAME = "md5";private static final int HASH_ITERATIONS = 2;public static String encrypt(String pswd) {String newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(SALT), HASH_ITERATIONS).toHex();return newPassword;}public static String encrypt(String username, String pswd) {String newPassword = new SimpleHash(ALGORITH_NAME, pswd, ByteSource.Util.bytes(username + SALT),HASH_ITERATIONS).toHex();return newPassword;}public static void main(String[] args) {// System.out.println(MD5Utils.encrypt("admin", "111111"));}}
对MD5加盐不太了解的可以看这篇文章使用Shiro的SimpleHash加密密码工具类_mr_foxsand的专栏-CSDN博客
关于ajax的页面跳转
使用AJAX实现页面跳转 - 南哥的天下 - 博客园
页面跳转的几种形式
-
window.parent.location
用于更改父窗口的位置。 -
window.top.location
- 它是对象'window'的属性。
- 它返回窗口层次结构中最顶层窗口的位置。
- 如果窗口没有父窗口,则top是对自身的引用(窗口=== window.top)
window.top.location.href 和 window.location.href 的区别 - 戒不掉n_思念 - 博客园
日志记录
使用aop横切日志
package com.bootdo.common.aspect;import com.bootdo.common.utils.HttpContextUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import sun.net.util.IPAddressUtil;import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;@Aspect
@Component
public class WebLogAspect {private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class);@Pointcut("execution( * com.bootdo..controller.*.*(..))")//两个..代表所有子目录,最后括号里的两个..代表所有参数public void logPointCut() {}@Before("logPointCut()")public void doBefore(JoinPoint joinPoint) throws Throwable {// 接收到请求,记录请求内容ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();// 记录下请求内容logger.info("请求地址 : " + request.getRequestURL().toString());logger.info("HTTP METHOD : " + request.getMethod());// 获取真实的ip地址//logger.info("IP : " + IPAddressUtil.getClientIpAddress(request));logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."+ joinPoint.getSignature().getName());logger.info("参数 : " + Arrays.toString(joinPoint.getArgs()));
// loggger.info("参数 : " + joinPoint.getArgs());}@AfterReturning(returning = "ret", pointcut = "logPointCut()")// returning的值和doAfterReturning的参数名一致public void doAfterReturning(Object ret) throws Throwable {// 处理完请求,返回内容(返回值太复杂时,打印的是物理存储空间的地址)logger.debug("返回值 : " + ret);}@Around("logPointCut()")public Object doAround(ProceedingJoinPoint pjp) throws Throwable {long startTime = System.currentTimeMillis();Object ob = pjp.proceed();// ob 为方法的返回值logger.info("耗时 : " + (System.currentTimeMillis() - startTime));return ob;}
}
Spring AOP 中@Pointcut的用法 - 山高我为峰 - 博客园