公司网站百度推广,动漫设计与制作是什么,重庆市工程新希望官网,wordpress安全配置文件在前面两篇#xff0c;我们基本上实现了图书管理系统所有的功能#xff0c;但是我们发现没有登录也能对其进行修改。这是非常不安全的。因此这篇文章我们学习如何进行强制登录。只有登录进去才能进行操作。 这不是一个对外开放的项目 这篇文章我们将改写图书管理系统为强制登… 在前面两篇我们基本上实现了图书管理系统所有的功能但是我们发现没有登录也能对其进行修改。这是非常不安全的。因此这篇文章我们学习如何进行强制登录。只有登录进去才能进行操作。 这不是一个对外开放的项目 这篇文章我们将改写图书管理系统为强制登录版本。我们需要结合在统一功能中讲解的
拦截器、统一数据返回、统一异常处理。内容完整的再写一遍图书管理系统
一、实现强制登录
1.1自定义拦截器 1.新建interceptor目录。创建LoginInterceptor类去实现HandlerInterceptor接口 注意要加上Component注解代表已经交给Spring去管理。这个类才会被Spring检测到 2.重写preHandle方法。返回 true-放行。false拦截 3.验证用户是否登录。 //验证用户是否登录HttpSession session request.getSession();UserInfo userInfo (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);if(userInfo null || userInfo.getId()1){response.sendRedirect(/login.html);return false;}return true; 如果没有登录则跳转到/login.html登录页面进行登录。return false如果已经登录那么直接return true。放行 1.2注册配置拦截器 1.新建Config目录创建WebConfig类 实现 WebMvcConfigurer 接口 注意要加上Configuration 才能生效。 2.新建我们刚刚自定义的拦截器成员变量 Autowiredprivate LoginInterceptor loginInterceptor; 3.重写addInterceptors方法。 addInterceptor添加拦截器 addPathPatterns添加拦截路径 excludePathPatterns除了这些路径不用拦截 Overridepublic void addInterceptors(InterceptorRegistry registry) {// /**表示对所有路径生效registry.addInterceptor(loginInterceptor).addPathPatterns(/**).excludePathPatterns(excludePath);} private ListString excludePath Arrays.asList(/user/login,/**/login.html,/css/**,/js/**,/pic/**); 自此。我们就实现了强制登录。 完整LoginInterceptor类代码
/*** 拦截器* 实现强制登录*/
Slf4j
Component
public class LoginInterceptor implements HandlerInterceptor {/*** 处理请求前执行。* param request* param response* param handler* return* throws Exception*/Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {log.info(目标方法执行前执行LoginInterceptor.preHandle...);//true-放行。false拦截//验证用户是否登录HttpSession session request.getSession();UserInfo userInfo (UserInfo) session.getAttribute(Constants.USER_SESSION_KEY);if(userInfo null || userInfo.getId()1){response.sendRedirect(/login.html);return false;}return true;}/*** 接口处理完成之后执行的方法* param request* param response* param handler* param modelAndView* throws Exception*/Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);}
}
完整WebConfig类代码
package com.qiyangyang.springbook.demos.Config;import com.qiyangyang.springbook.demos.interceptor.LoginInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.Arrays;
import java.util.List;//配合拦截器进行拦截处理
//注意一定要加上Configuration这个注解 才会生效
Configuration
public class WebConfig implements WebMvcConfigurer {private ListString excludePath Arrays.asList(/user/login,/**/login.html,/css/**,/js/**,/pic/**);Autowiredprivate LoginInterceptor loginInterceptor;Overridepublic void addInterceptors(InterceptorRegistry registry) {// /**表示对所有路径生效registry.addInterceptor(loginInterceptor).addPathPatterns(/**).excludePathPatterns(excludePath);}// Override
// public void addInterceptors(InterceptorRegistry registry) {
// // /** 表示对所有的路径生效
// registry.addInterceptor(loginInterceptor).addPathPatterns(/**)
// //排除一些路径
// .excludePathPatterns(/user/login)
// .excludePathPatterns(/**/**.html)
// .excludePathPatterns(/css/**)
// .excludePathPatterns(/js/**)
// .excludePathPatterns(/pic/**);
// }
}二、实现统一返回数据
2.1创建Result类 统一返回数据 1.首先创建Result类这个类帮我们更加细致化的对后端返回结果进行描述 package com.qiyangyang.springbook.demos.model;import lombok.Data;/*** 定义统一返回数据* 对返回结果进行细致化描述* param T*/Data
public class Result T{private Integer code;//后端响应状态码业务状态码 200成功-1失败-2表示未登录private String Errmsg; //后端发生错误的原因private T data; //返回的数据public static TResultT success(T data){ResultT result new ResultT();result.setCode(200);result.setData(data);return result;}public static TResultT fail(T data,String errmg){ResultT result new ResultT();result.setCode(-1);result.setErrmsg(errmg);result.setData(data);return result;}public static T ResultT fail(String errMsg){ResultT result new ResultT();result.setCode(-1);result.setErrmsg(errMsg);return result;}/*** 未登录时*/public static T ResultT unlogin(){ResultT result new ResultT();result.setCode(-2);result.setErrmsg(用户未登录);return result;}}2.2创建ResponseAdvice类 创建ResponseAdvice并实现ResponseBodyAdvice接口 * 统一数据返回格式 * 不需要改变程序任何接口只需要写一个类。实现ResponseBodyAdvice。加上ControllerAdvice注解 ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {Autowiredprivate ObjectMapper objectMapper;Overridepublic boolean supports(MethodParameter returnType, Class converterType) {//重写supports方法。看对哪些请求进行处理。返回true--进行处理 返回false。不进行处理return true;}SneakyThrowsOverridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装//接口执行完成之后返回结果之前会执行这个方法。//注意需要处理String类型的if (body instanceof Result){return body;}if (body instanceof String){return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);}
} //重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装 //接口执行完成之后返回结果之前会执行这个方法。beforeBodyWrite //注意需要处理String类型的 此时我们会发现。我们之前定义的后端接口的返回结果已经不再适用。 例如 我们进行用户登录 发现返回的数据是这种json类型的 三、修改前端代码
登录页代码 将判断条件修改为 if(result.code200 result.data true){//验证成功location.href book_list.html;}else {alert(登录失败用户名不存在或密码错误);} 图书列表显示页代码 将 var books result.records; 修改为 var books result.data.records; function getBookList() {//ajax页面一加载就会被执行的程序。 我们还可以进行方法封装$.ajax({type: get,url: /book/getListByPage location.search,success: function (result) {var books result.data.records;console.log(books); //如果前端没有报错那么我们打印日志。观察后端返回结果对不对var findHtml ; //用这个变量来拼接HTMLfor (var book of books) {//拼接html。假如后端返回10个tr那么直接for循环拼接在这里面。findHtml//我们用单引号拼接因为里面有双引号 图书分页代码 将 //翻页信息$(#pageContainer).jqPaginator({totalCounts: result.count, //总记录数pageSize: 10, //每页的个数visiblePages: 5, //可视页数currentPage: result.pageRequest.currentPage, //当前页码 修改为 //翻页信息$(#pageContainer).jqPaginator({totalCounts: result.data.count, //总记录数pageSize: 10, //每页的个数visiblePages: 5, //可视页数currentPage: result.data.pageRequest.currentPage, //当前页码 前端添加图书代码 注意这里有一个坑 我们照上面修改
将 success: function (result) {console.log(result.data);if (result 成功添加图书) {alert(添加成功);location.href book_list.html;} else {alert(result.data);}
修改为 success: function (result) {console.log(result.data);if (result.code 200 result.data 成功添加图书) {alert(添加成功);location.href book_list.html;} else {alert(result.data);} 会发现添加图书功能依然完成不了。且会执行else块的代码 并且报错为undefind。 这是由于返回的数据是String类型。 而上面我们进行处理的后端返回数据都是json类型。如果是json类型。那么前端会自动转换为对象进行处理。 而Sring就不会进行处理。因此代码没有按我们的预期执行。 我们通过Postman和Fiddler抓包也能看到返回的是text类型文本。而不是json。 而我们登录接口返回的是json 两种解决方式 1.前端处理把字符串转为对象 2.后端处理设置 content-type 返回结果 更加合理 这种问题也称作边界问题。 后端处理我们可以进行统一返回Json。这样更加合理。 为什么String类型会出现这种情况。 这个就是在我们统一返回结果类中进行处理的。 SneakyThrowsOverridepublic Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {//重写beforeBodyWrite方法。如果body是Result类型。不再进行包装。如果不是。那么就进行包装//接口执行完成之后返回结果之前会执行这个方法。//注意需要处理String类型的if (body instanceof Result){return body;}if (body instanceof String){return objectMapper.writeValueAsString(Result.success(body));}return Result.success(body);} 发现body如果是String类型。我们将这个body进行了writeValueAsString转成了String 因此我们将 RequestMapping(/addBook)public String addBook(BookInfo bookInfo){log.info(添加图书bookInfo:{},bookInfo);/**
修改为 RequestMapping(value /addBook,produces application/json)public String addBook(BookInfo bookInfo){log.info(添加图书bookInfo:{},bookInfo);/** 这样我们返回的数据就是Json格式的了。再试试添加图书就会发现我们添加成功 因此我们不用返回String类型的就可以避免这个问题了 正确的返回我们本就应该返回为Result类型。试着修改代码如下。 RequestMapping( /addBook)public Result addBook(BookInfo bookInfo){log.info(添加图书bookInfo:{},bookInfo);/*** 参数校验*/if(!StringUtils.hasLength(bookInfo.getBookName())|| !StringUtils.hasLength(bookInfo.getAuthor())|| !StringUtils.hasLength(bookInfo.getPublish())|| bookInfo.getCount() 0|| bookInfo.getPrice()null){return Result.fail(参数错误);}/*** 添加图书*//*** 出现错误比如参数错误。添加过程出现异常因为MySQL会出现一些异常* 我们使用try/catch来捕获。*/try {bookService.insertBook(bookInfo);}catch (Exception e){log.error(添加图书失败e:{},e);return Result.fail(内部发生错误请联系管理员);}/*** 返回成功添加吐过表示图书插入成功。*/return Result.success(成功添加图书);}
修改图书代码 查询当前 Id 图书代码修改 //查询当前ID图书$.ajax({type: get,url: book/queryBookByIdlocation.search,success:function(book){if(book!null){$(#bookId).val(book.id);$(#bookName).val(book.bookName);$(#bookAuthor).val(book.author);$(#bookStock).val(book.count);$(#bookPrice).val(book.price);$(#bookPublisher).val(book.publish);$(#bookStatus).val(book.status);}} 修改为 type: get,url: book/queryBookByIdlocation.search,success:function(result){//前端根据后端返回结果针对不同的情况进行处理if(result.code 200 result.data ! null){var book result.data;if(book!null){$(#bookId).val(book.id);$(#bookName).val(book.bookName);$(#bookAuthor).val(book.author);$(#bookStock).val(book.count);$(#bookPrice).val(book.price);$(#bookPublisher).val(book.publish);$(#bookStatus).val(book.status);}} 修改更新图书代码 type: get,url: /book/updateBook,data:$(#updateBook).serialize(),//提交整个表单success:function(result){if(result true){alert(更新成功);location.href book_list.html}else{alert(更新失败);} 修改为 type: get,url: /book/updateBook,data:$(#updateBook).serialize(),//提交整个表单success:function(result){if(result.code200 result.data true){alert(更新成功);location.href book_list.html}else{alert(更新失败);}
删除图书代码 if(result true){alert(删除成功);location.href book_list.html;} 修改为 if(result.code 200 result.data true){alert(删除成功);location.href book_list.html;}
批量删除图书代码 if(result true){alert(批量删除成功);location.href book_list.html;}else{alert(删除失败请联系管理员);}
修改为 if(result.code 200 result.data true){alert(批量删除成功);location.href book_list.html;}else{alert(删除失败请联系管理员);}
四、统一异常处理
ControllerAdvice
public class ErrorHandler {/*** 捕获异常返回统一结果*///通过ExceptionHandler这个注解来捕获异常ExceptionHandlerpublic Result handler(Exception e){return Result.fail(e.getMessage());}
}