赞
踩
博客网站项目是一个SSM项目,项目主要是模仿CSDN博客网站,主要实现了用户登录、用户注册、写博客、删除博客、修改博客、发布博客、查看看博客等功能。使用了第三方工具类hutool实现了登录页面验证码功能,使用加盐算法对用户密码进行加密保存,使用拦截器实现了统一登入验证功能,使用Redis解决了Session持久化问题,通过MyBatis来操作数据库实现了用户注册保存用户信息、写博客保存博客信息等功能。
HTML、CSS、JS、Ajax、SpringBoot、SpringMVC、MyBatis
在博客网站中有的页面是可以在没有登录的状态下打开的,有的页面只有在登录的状态下才能打开。
所以,我们需要实现统一登录的功能。
这里我使用拦截器来实现统一验证功能
实现拦截器的步骤:
public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(false); System.out.println("进拦截器"); if (session != null && session.getAttribute(MyConstant.SESSION_LOGIN_KEY)!= null) { //有用户登入信息,放行 return true; } response.setStatus(401); System.out.println("出拦截器"); //非法用户,拦截 return false; } }
@Configuration public class MyLoginCondig implements WebMvcConfigurer { /** * 添加拦截器 * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/**") // 设置要拦截的路径,这样设置所以的都拦截 .excludePathPatterns("/css/**") .excludePathPatterns("/editor.md/**") .excludePathPatterns("/img/**") .excludePathPatterns("/auto_img/**") .excludePathPatterns("/js/**") .excludePathPatterns("/**/*.html") .excludePathPatterns("/user/reg") .excludePathPatterns("/user/login") .excludePathPatterns("/article/blog_list") .excludePathPatterns("/article/total_page") .excludePathPatterns("/article/blog_list_content_detail") .excludePathPatterns("/user/code") .excludePathPatterns("/user/userinfo"); } }
我们希望项目的可靠性是要强的,如果项目发生了异常,项目不至于终止,项目还能正常运行。所以我们需要对异常做一些处理。
在博客网站中,我们对异常的处理是项目出现异常返回异常信息并且code设置为-1.
后端代码:
/**
* 统一异常处理
*/
@ControllerAdvice
@ResponseBody
public class ErrorAdvice {
@ExceptionHandler(Exception.class)
public HashMap<String,Object> errorAdvice(Exception e){
return AjaxResult.fail(-1,e.getMessage());
}
}
我们返回给客户端的数据并不是五花八门的,一会儿是字符串,一会儿是JSON格式,这样前端的程序员就不会很好的接收响应数据。所以,我们就对返回的数据统一处理成相同的数据。
后端代码:
/** * 同一数据格式处理 */ @ControllerAdvice public class ResponseAdvice implements ResponseBodyAdvice { @Override public boolean supports(MethodParameter returnType, Class converterType) { return true; } @SneakyThrows @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof HashMap) { //数据已经是封装好的,直接返回就行了 return body; } if (body instanceof String) { //通过jackson依赖提供的ObjectMapper类型的对象,手动的将数据转为JSON格式 ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.writeValueAsString(AjaxResult.success(body)); } return AjaxResult.success(body); } }
为了防止恶意破解密码、刷票、论坛灌水,有效防止某个黑客对某一个特定注册用户用特定程序暴力破解方式进行不断的登陆尝试,我在登录页面添加了验证码的这一项功能。
生成验证码的方法: 在该博客网站项目中,我是使用第三方工具类hutool 来生成验证码。
后端代码:
/** * 生成验证码功能 * @param request * @param response */ @RequestMapping("/code") @ResponseBody public void getCode(HttpServletRequest request, HttpServletResponse response) throws IOException { //服务器通知浏览器不要缓存 response.setHeader("pragma","no-cache"); response.setHeader("cache-control","no-cache"); response.setHeader("expires","0"); //利用hutool工具,生成验证码图片资源 LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(120, 50, 4, 35); // 获取生成的验证码字符串 String code = lineCaptcha.getCode(); // 利用session来存储 验证码 HttpSession session = request.getSession(); session.setAttribute("code",code); //将验证码图片的二进制数据放进响应体中 lineCaptcha.write(response.getOutputStream()); }
我们在保存密码的时候并不是明文保存的,这样会存在安全性问题。我们是对密码进行加密后保存。所以,我底层写了一个类进行加密和解密操作。
**加密操作的思路:**随机生成32位数字作为盐值(通过Java自带UUID.randomUUID()方法生成随机的盐值),然后将盐值和密码拼接起来通过md5进行非对称加密生成32位新密码,然后将新密码和盐值一起保存到数据库
***解密操作的思路:***获取到数据库中相对应的盐值,将盐值与要确认的密码进行拼接,然后进行md5进行非对称加密生成的字符串,生成的字符串在拼接盐值,在与数据库中保存的密码进行比较。
后端代码:
/** * 加盐加密类 * (1)加密方法 * (2)解密方法 */ public class SecurityUtil { /** * 加密方法 * 思路: * 通过Java自带的UUID.randomUUID()方法生成随机的盐值, * UUID.randomUUID()生成字符串的格式是:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx * 我们可以把 - 给去掉 * spring中有提供一个加密工具类DigestUtils,我们可以进行md5加密,加密后的密码是32位 * md5加密:非对称加密,只要要加密的密码相同,他们加密后的密码是一样的。 * * @param password * @return */ public static String encrypt(String password) { //使用UUID.randomUUID()产生盐值,并且去除盐值中的 -, //并且产生的字符串的长度是32位 String salt = UUID.randomUUID().toString().replaceAll("-",""); //salt+pqssword一起加密,这样形参的加密密码进不一样了,因为每一次盐值都是不一样的 String finalPassWord = DigestUtils.md5DigestAsHex((salt+password).getBytes()); //返回的字符串是盐值+最终的密码,这个字符串是64位 //盐值在前,加密后的密码在后 return salt+finalPassWord; } /** * 密码解析,判断用户输入的密码是否是正确的 * @param password * @param finalPassoWrod * @return */ public static boolean decode(String password,String finalPassoWrod) { //非空校验 if (!StringUtils.hasLength(password)||!StringUtils.hasLength(finalPassoWrod)) { //参数非法 return false; } //先将finalPassoWrod进行切割,分离出前32位,得到盐值 String salt = finalPassoWrod.substring(0,32); //salt+password进行md5加密 String newFinalPassword = DigestUtils.md5DigestAsHex((salt+password).getBytes()); //我们通过 盐值+盐值和我们要确认的密码生成新的 加密密码 与 保存数据库的密码进行比较 return (salt+newFinalPassword).equals(finalPassoWrod); } }
解决方法:将前端传来的图片保存到resources/static/auto_img文件夹下,数据库中保存图片的路径。
后端代码:
获取到resources/static/auto_img文件路径的代码:
/**
* 获取到SpringBoot项目的resources目录的路径
*/
public class GetSavePath {
public String getSavePath() {
//注意:ApplicationHome类属于SpringBoot提供的类
ApplicationHome applicationHome = new ApplicationHome(this.getClass());
//获取resources\static\auto_img的目录
return applicationHome.getDir().getParentFile()
.getParentFile().getAbsolutePath()+"\\src\\main\\resources\\static\\auto_img";
}
}
controller层代码:
/**
* 对图像进行保存
*/
@RequestMapping("/file_img")
public String fileHeader(@RequestPart("file_img")MultipartFile file,HttpServletRequest request){
if (!file.isEmpty()) {
Userinfo userinfo = (Userinfo) request.getSession(false).getAttribute(MyConstant.SESSION_LOGIN_KEY);
userinfoService.fileHeader(file,userinfo.getId());
}
//重定向到
return "redirect:/person_center.html";
}
service层代码:
/** * 保存图片 * @param file */ public void fileHeader(MultipartFile file,Integer id){ //将文件进行重命名,避免文件名字相同 //使用UUID.randomUUID()生成随机的名字 String fileName = UUID.randomUUID().toString().replaceAll("-","")+"."+file.getContentType().split("/")[1]; //获取要保存到的路径 String path = getSavePath.getSavePath(); //将图片保存到相对应的目录中 File file1 = new File(path,fileName); //判断文件是否存在,不存在创建 if (!file1.exists()) { try { file1.createNewFile(); //保存到相对应的目录里 file.transferTo(file1); } catch (IOException e) { e.printStackTrace(); } } //将路径保存到数据库中 userinfoMapper.updatePath("./auto_img/"+fileName,id); // System.out.println(path+"\\"+fileName); }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。