4. 组件设计
组件包括MD5加密、图片重命名、图片上传配置、全局常量以及异常枚举、自定义异常和全局异常处理。
4.1 工具类
4.1.1 MD5加密类【MD5Utils】
列举了三种生成方式,项目里使用的是方法一。
package pers.xsc.online_shopping.utils;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.apache.tomcat.util.codec.binary.Base64;
import org.springframework.util.DigestUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 密码加密工具类
* 密码比较时只需将用户输入的密码再次转成MD5码与存储的相比较即可得知用户输入密码是否正确。
*/
public class MD5Utils {
//方式一:通过Java自带的MessageDigest实现
public static String getMD5StrByMessageDigest(String strValue) throws NoSuchAlgorithmException {
MessageDigest md5 = MessageDigest.getInstance(Constant.ALGORITHM_NAME);
return Base64.encodeBase64String(md5.digest((strValue + Constant.pwd_salt).getBytes()));
}
//方式二:通过Spring中的DigestUtils工具类
public static String getMD5StrByDigestUtils(String strValue) {
return DigestUtils.md5DigestAsHex((strValue + Constant.pwd_salt).getBytes());
}
//方式三:使用Shiro的SimpleHash加密密码工具类:需要添加shiro-core依赖
/**
* algorithmName:加密形式(具体支持哪些算法,请自行百度)
* source:简单理解就是传入的原始明文密码值
* salt:盐值
* hashIterations:加密次数
* 最终得到加密的密码 = MD5(明文密码 + 盐值)* 加密次数
*/
public static String getMD5StrBySimpleHash(String strValue) {
return new SimpleHash(Constant.ALGORITHM_NAME, strValue, ByteSource.Util.bytes(Constant.pwd_salt), Constant.HASH_ITERATIONS).toHex();
}
//测试
public static void main(String[] args) throws NoSuchAlgorithmException {
String pwd = getMD5StrByMessageDigest("123456");
System.out.println(pwd);
String pwd2 = getMD5StrByDigestUtils("123456");
System.out.println(pwd2);
String pwd3 = getMD5StrBySimpleHash("123456");
System.out.println(pwd3);
}
}
4.1.2 全局常量类【Constant】
package pers.xsc.online_shopping.utils;
/**
* 常量类
*/
public class Constant {
// 加密形式
public static final String ALGORITHM_NAME = "MD5";
// 加密次数
public static final int HASH_ITERATIONS = 128;
//MD5加密盐值
public static final String pwd_salt = "hfg6594ha36546/*--*634" ;
//验证码参数-生成元素
public static final char CODE[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
'k', 'm', 'n', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M',
'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2',
'3', '4', '5', '6', '7', '8', '9' };
//生成宽度
public static final int WIDTH = 50;
//生成高度
public static final int HEIGHT = 20;
//生成字符数
public static final int LENGTH = 4;
//前台用户session名
public static final String CURRENT_USER = "currentUser";
//后台管理员session名
public static final String ADMIN_USER = "adminUser";
}
4.1.3 图片重命名类(带后缀)【ReFilenameUtils】
package pers.xsc.online_shopping.utils;
import org.apache.commons.lang.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Calendar;
public class ReFilenameUtils {
/**
* 文件重命名(带后缀)
* @param fileName 原文件名
* @return 新文件名
*/
public static String replaceFileName(String fileName) {
StringBuilder builder = new StringBuilder();
if (StringUtils.isNotEmpty(fileName)) {
int index = fileName.lastIndexOf(".");
if (index > 0) {
SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String fileType = fileName.substring(index);// 文件后缀名
Calendar cal = Calendar.getInstance();//获取当前的时间
builder.append(format.format(cal.getTime()));
builder.append(fileType);
}
}
return builder.toString();
}
//测试
public static void main(String[] args) {
System.out.println(replaceFileName("hello.java"));
}
}
4.1.4 图片上传配置类【UploadConfig】
用于解决springboot图片上传本地后需要重启服务器才能加载到图片的问题:
package pers.xsc.online_shopping.utils;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class UploadConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//映射图片保存地址
registry.addResourceHandler("/images/**").addResourceLocations("file:F:/online_shopping/online_shopping/src/main/resources/static/images/");
}
}
4.1.5 自定义结果类【ResultUtils】
package pers.xsc.online_shopping.utils;
import pers.xsc.online_shopping.exception.ExceptionEnum;
public class ResultUtils {
private Integer status; //状态码
private String msg; //异常描述
private Object data; //返回数据
public ResultUtils(Integer status, String msg, Object data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public ResultUtils(Integer status, String msg) {
this.status = status;
this.msg = msg;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public ResultUtils(ExceptionEnum exceptionEnum){
this.status = exceptionEnum.getCode();
this.msg = exceptionEnum.getMsg();
}
public static ResultUtils success(){
ResultUtils resultUtils = new ResultUtils(ExceptionEnum.SUCCESS);
return resultUtils;
}
public static ResultUtils success(Object data){
ResultUtils resultUtils = new ResultUtils(ExceptionEnum.SUCCESS);
resultUtils.setData(data);
return resultUtils;
}
public static ResultUtils error(){
ResultUtils resultUtils = new ResultUtils(ExceptionEnum.SYSTEM_ERROR);
return resultUtils;
}
public static ResultUtils error(Integer code, String msg) {
return new ResultUtils(code, msg);
}
public static ResultUtils error(ExceptionEnum exceptionEnum){
ResultUtils resultUtils = new ResultUtils(exceptionEnum);
return resultUtils;
}
@Override
public String toString() {
return "信息提醒{" +
"状态码=" + status +
",消息='" + msg + '\'' +
",数据=" + data +
'}';
}
}
4.2 组件类
4.2.1 验证码生成类【ValidateCodeController】
package pers.xsc.online_shopping.controller.before;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pers.xsc.online_shopping.utils.Constant;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
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.io.IOException;
import java.io.OutputStream;
import java.util.Random;
@Controller
public class ValidateCodeController {
@RequestMapping("/validateCode")
public void validateCode(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
// 设置响应报头信息
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
// 设置响应的MIME类型
response.setContentType("image/jpeg");
BufferedImage image = new BufferedImage(Constant.WIDTH, Constant.HEIGHT,
BufferedImage.TYPE_INT_RGB);
Font mFont = new Font("Arial", Font.TRUETYPE_FONT, 18);
Graphics g = image.getGraphics();
Random rd = new Random();
// 设置背景颜色
g.setColor(new Color(rd.nextInt(55) + 200, rd.nextInt(55) + 200, rd
.nextInt(55) + 200));
g.fillRect(0, 0, Constant.WIDTH, Constant.HEIGHT);
// 设置字体
g.setFont(mFont);
// 画边框
g.setColor(Color.black);
g.drawRect(0, 0, Constant.WIDTH - 1, Constant.HEIGHT - 1);
// 随机产生的验证码
String result = "";
for (int i = 0; i < Constant.LENGTH; ++i) {
result += Constant.CODE[rd.nextInt(Constant.CODE.length)];
}
HttpSession se = request.getSession();
se.setAttribute("rand", result);
// 画验证码
for (int i = 0; i < result.length(); i++) {
g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd
.nextInt(200)));
g.drawString(result.charAt(i) + "", 12 * i + 1, 16);
}
// 随机产生2个干扰线
for (int i = 0; i < 2; i++) {
g.setColor(new Color(rd.nextInt(200), rd.nextInt(200), rd
.nextInt(200)));
int x1 = rd.nextInt(Constant.WIDTH);
int x2 = rd.nextInt(Constant.WIDTH);
int y1 = rd.nextInt(Constant.HEIGHT);
int y2 = rd.nextInt(Constant.HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
// 释放图形资源
g.dispose();
try {
OutputStream os = response.getOutputStream();
// 输出图像到页面
ImageIO.write(image, "JPEG", os);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.2.2 异常枚举类【ExceptionEnum】
package pers.xsc.online_shopping.exception;
/**
* 异常枚举
*/
public enum ExceptionEnum {
SYSTEM_ERROR(00000, "系统异常,请从控制台或日志中查看具体错误信息"),
SUCCESS(10000,"成功!"),
NEED_LOGIN(20001, "用户未登录"),
USER_LOGIN_ERROR(20002, "账号不存在或密码错误"),
PARAM_IS_BLANK(20003,"参数[用户名/密码]为空"),
OBJECT_IS_BLANK(20004,"对象为空"),
SESSION_FAILED(20005,"session失效,须重新登录"),
REQUEST_PARAM_ERROR(30001,"参数错误"),
INSERT_ERROR(40001,"数据添加失败");
private Integer code;
private String msg;
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
ExceptionEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String toString() {
return "ExceptionEnum{" +
"code=" + code +
", msg='" + msg + '\'' +
'}';
}
}
4.2.3 自定义异常类【CustomException】
package pers.xsc.online_shopping.exception;
public class CustomException extends RuntimeException{
private Integer code;
private String message;
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public CustomException(Integer code, String message) {
this.code = code;
this.message = message;
}
}
4.2.4 全局异常处理类【ExceptionHandle】
package pers.xsc.online_shopping.exception;
import jdk.internal.org.objectweb.asm.tree.analysis.Value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import pers.xsc.online_shopping.utils.ResultUtils;
import java.util.ArrayList;
import java.util.List;
@Slf4j
@ControllerAdvice
public class ExceptionHandle {
@ExceptionHandler(value = Exception.class)
public String handleException(Exception e , Model model){
log.info("Exception",e);
model.addAttribute("errorMessage",ResultUtils.error(ExceptionEnum.SYSTEM_ERROR).toString());
return "Error";
}
//处理自定义的异常
@ExceptionHandler(value = CustomException.class)
public String handleCustomException(CustomException e , Model model){
log.info("CustomException",e);
model.addAttribute("errorMessage",ResultUtils.error(e.getCode(),e.getMessage()).toString());
return "Error";
}
//注解参数校验异常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public String handleMethodArgumentNotValidException(MethodArgumentNotValidException e , Model model){
log.info("MethodArgumentNotValidException",e);
model.addAttribute("errorMessage",handleBindingResult(e.getBindingResult()).toString());
return "Error";
}
private ResultUtils handleBindingResult(BindingResult result) {
//把异常处理为对外暴露的提示
List<String> list = new ArrayList<>();
if (result.hasErrors()) {
List<ObjectError> allErrors = result.getAllErrors();
for (ObjectError objectError : allErrors) {
String message = objectError.getDefaultMessage();
list.add(message);
}
}
if (list.size() == 0) {
return ResultUtils.error(ExceptionEnum.REQUEST_PARAM_ERROR);
}
return ResultUtils.error(ExceptionEnum.REQUEST_PARAM_ERROR.getCode(), list.toString());
}
}
4.2.5 用户过滤器类【UserFilter】
package pers.xsc.online_shopping.utils;
import pers.xsc.online_shopping.entity.User;
import pers.xsc.online_shopping.exception.CustomException;
import pers.xsc.online_shopping.exception.ExceptionEnum;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
public class UserFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpSession session = request.getSession();
User currentUser = (User) session.getAttribute(Constant.CURRENT_USER);
try{
if (currentUser == null) {
throw new CustomException(ExceptionEnum.NEED_LOGIN.getCode(),ExceptionEnum.NEED_LOGIN.getMsg());
}else{
filterChain.doFilter(servletRequest, servletResponse);
}
}catch(CustomException e){
request.setAttribute("filter.error", ExceptionEnum.NEED_LOGIN);
request.getRequestDispatcher("/myError").forward(request, servletResponse);
}
}
@Override
public void destroy() {
Filter.super.destroy();
}
}
4.2.5 用户过滤器配置类【UserFilterConfig】
package pers.xsc.online_shopping.utils;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UserFilterConfig {
@Bean
public UserFilter userFilter() {
return new UserFilter();
}
@Bean(name = "userFilterConf")
public FilterRegistrationBean adminFilterConfig() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(userFilter());
filterRegistrationBean.addUrlPatterns("/focus/myFocus");
filterRegistrationBean.addUrlPatterns("/user/userInfo");
filterRegistrationBean.addUrlPatterns("/cart/selectCart");
return filterRegistrationBean;
}
}
4.2.6 捕获过滤器中产生异常类【MyErrorController】
package pers.xsc.online_shopping.controller.before;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import pers.xsc.online_shopping.exception.CustomException;
import pers.xsc.online_shopping.exception.ExceptionEnum;
import javax.servlet.http.HttpServletRequest;
@Controller
public class MyErrorController {
@RequestMapping("/myError")
public void error(HttpServletRequest request){
ExceptionEnum e = (ExceptionEnum) request.getAttribute("filter.error");
throw new CustomException(e.getCode(),e.getMsg());
}
}