【项目】CRM系统中基础模块,异常处理封装,非法请求拦截处理办法

大家好,我是小冷
上一篇介绍了CRM是什么?以及CRM系统有哪些模块组成。需要详细的地址点击:地址
接下来继续介绍CRM系统的具体模块逻辑信息

基础模块

每个系统都有自己的基础模块,CRM系统也有,主要包括登录,退出,记住密码,忘记密码等等。

登录功能

UserController层写的都是用户相关的接口,控制层定义接口,对接前台。Controller 层调用 Service 层 userLogin 方法,捕获 service 方法的异常,获取登录结果,并将 ResultInfo 对象通过 JSON 格式响应给客户端。
可以看下源代码:

@Controller
@RequestMapping("user")
public class UserController extends BaseController {

    @Resource
    private UserService userService;


    /**
     * 用户登录
     *
     *
     * @param userName
     * @param userPwd
     */
    @PostMapping("login")
    @ResponseBody
    public ResultInfo userLogin(String userName, String userPwd) {

        ResultInfo resultInfo = new ResultInfo();

        // 调用service层登录方法
        UserModel userModel = userService.userLogin(userName, userPwd);

        // 设置ResultInfo的result的值 (将数据返回给请求)
        resultInfo.setResult(userModel);
        
        return resultInfo;
    }
}

postMan测试结果如下:

在这里插入图片描述
前端登录功能实现
index.ftl 添加对应 index.js,使用 layui 表单组件实现表单提交操作。登录成功后,如果
参考API:https://www.layui.com/doc/modules/form.html#onsubmit

layui.use(['form','jquery','jquery_cookie'], function () {
    var form = layui.form,
        layer = layui.layer,
        $ = layui.jquery,
        $ = layui.jquery_cookie($);
    /**
     * 用户登录 表单提交
     */
    form.on("submit(login)", function(data){
        // 获取表单元素的值 (用户名 + 密码)
        var fieldData = data.field;
        // 判断参数是否为空
        if (fieldData.username == "undefined" || fieldData.username.trim() == "") {
            layer.msg("用户名称不能为空!");
            return false;
       }
        if (fieldData.password == "undefined" || fieldData.password.trim() == "") {
            layer.msg("用户密码不能为空!");
            return false;
       }
        // 发送 ajax 请求,请求用户登录
        $.ajax({
            type:"post",
            url:ctx + "/user/login",
            data:{
                userName:fieldData.username,
                userPwd:fieldData.password
           },
            dataType:"json",
            success:function(data){
                // 判断是否登录成功
                if (data.code == 200) {
                    layer.msg("登录成功!", function () {
                        // 将用户信息存到cookie中
                        var result = data.result;
                        $.cookie("userId", result.userId);
                        $.cookie("userName", result.userName);
                        $.cookie("trueName", result.trueName);
                        // 登录成功后,跳转到首页
                        window.location.href = ctx + "/main";
                   });
               } else {
                    // 提示信息
                    layer.msg(data.msg);
               }
           }
       });
        // 阻止表单跳转
        return false;
   });
});

密码修改

核心思路分析

用户密码修改
1. 参数校验
userId 非空 用户对象必须存在
oldPassword 非空 与数据库中密文密码保持一致
newPassword 非空 与原始密码不能相同
confirmPassword 非空 与新密码保持一致
2. 设置用户新密码
新密码进行加密处理
3. 执行更新操作
受影响的行数小于1,则表示修改失败
UserService中updateUserPassword 方法实现

@Transactional(propagation = Propagation.REQUIRED)
public void updateUserPassword (Integer userId, String oldPassword, 
                                String newPassword, String confirmPassword ) {
    // 通过userId获取用户对象
    User user = userMapper.selectByPrimaryKey(userId);
    // 1. 参数校验
    checkPasswordParams(user, oldPassword, newPassword, confirmPassword);
    // 2. 设置用户新密码
    user.setUserPwd(Md5Util.encode(newPassword));
    // 3. 执行更新操作
    AssertUtil.isTrue(userMapper.updateByPrimaryKeySelective(user) < 1, "用户密码更新失败!");
}

准备密码修改页对应 JS 文件。添加表单提交代码,使用 ajax 对接后端密码修改接口实现密码修改操
作, 当密码修改后清除客户端 cookie 信息并跳转至登录页面。
在 src/main/resources/public/ js 目录中,新建 user 目录,添加 password.js 文件

layui.use(['form','jquery','jquery_cookie'], function () {
    var form = layui.form,
        layer = layui.layer,
        $ = layui.jquery,
        $ = layui.jquery_cookie($);
    /**
     * 用户密码修改 表单提交
     */
    form.on("submit(saveBtn)", function(data) {
        // 获取表单元素的内容
        var fieldData = data.field;
        // 发送ajax请求,修改用户密码
        $.ajax({
            type:"post",
            url:ctx + "/user/updatePassword",
            data:{ oldPassword:fieldData.old_password,
                newPassword:fieldData.new_password,
                confirmPassword:fieldData.again_password
           },
            dataType:"json",
            success:function (data) {
                // 判断是否成功
                if (data.code == 200) {
                    // 修改成功后,用户自动退出系统
                    layer.msg("用户密码修改成功,系统将在3秒钟后退出...", function () {
                        // 退出系统后,删除对应的cookie
                        $.removeCookie("userIdStr",{domain:"localhost",path:"/crm"});
                        $.removeCookie("userName", {domain:"localhost",path:"/crm"});
                        $.removeCookie("trueName", {domain:"localhost",path:"/crm"});
                        // 跳转到登录页面 (父窗口跳转)
                        window.parent.location.href = ctx + "/index";
                   });
               } else {
                    layer.msg(data.msg);
               }
           }
       });
   });
});

退出登录

找到 “退出登录” 的元素,并绑定点击事件。当用户点击退出时,清空cookie信息

在 main.js 中,通过类选择器绑定元素的点击事件

/**
 * 用户退出
 * 删除cookie
 */
$(".login-out").click(function () {
  // 删除 cookie
  $.removeCookie("userIdStr", {domain:"localhost",path:"/crm"});
  $.removeCookie("userName", {domain:"localhost",path:"/crm"});
  $.removeCookie("trueName", {domain:"localhost",path:"/crm"});
  // 跳转到登录页面 (父窗口跳转)
  window.parent.location.href = ctx + "/index";
});

全局异常处理封装

实现思路:

控制层的方法返回的内容两种情况:1. 视图:视图异常 2. Json:方法执行错误 返回错误json信息

全局异常拦截器实现
实现 HandlerExceptionResolver 接口 ,处理应用程序异常信息

/**
 * 全局异常统一处理
 *
 */
@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
    /**
     * 异常处理方法
     *  方法的返回值:
     *      1. 返回视图
     *      2. 返回数据(JSON数据)
     *
     *  如何判断方法的返回值?
     *      通过方法上是否声明@ResponseBody注解
     *          如果未声明,则表示返回视图
     *          如果声明了,则表示返回数据
    
     * @param request request请求对象
     * @param response  response响应对象
     * @param handler 方法对象
     * @param ex 异常对象
     * @return org.springframework.web.servlet.ModelAndView
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {

        /**
         * 非法请求拦截
         *  判断是否抛出未登录异常
         *      如果抛出该异常,则要求用户登录,重定向跳转到登录页面
         */
        if (ex instanceof NoLoginException) {
            // 重定向到登录页面
            ModelAndView mv = new ModelAndView("redirect:/index");
            return mv;
        }


        /**
         * 设置默认异常处理(返回视图)
         */
        ModelAndView modelAndView = new ModelAndView("error");
        // 设置异常信息
        modelAndView.addObject("code",500);
        modelAndView.addObject("msg","系统异常,请重试...");


        // 判断HandlerMethod
        if (handler instanceof HandlerMethod) {
            // 类型转换
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // 获取方法上声明的@ResponseBody注解对象
            ResponseBody responseBody = handlerMethod.getMethod().getDeclaredAnnotation(ResponseBody.class);

            // 判断ResponseBody对象是否为空 (如果对象为空,则表示返回的事视图;如果不为空,则表示返回的事数据)
            if (responseBody == null) {
                /**
                 * 方法返回视图
                 */
                // 判断异常类型
                if (ex instanceof ParamsException) {
                    ParamsException p = (ParamsException) ex;
                    // 设置异常信息
                    modelAndView.addObject("code",p.getCode());
                    modelAndView.addObject("msg",p.getMsg());

                } else if (ex instanceof AuthException) { // 认证异常
                    AuthException a  = (AuthException) ex;
                    // 设置异常信息
                    modelAndView.addObject("code",a.getCode());
                    modelAndView.addObject("msg",a.getMsg());
                }

                return modelAndView;

            } else {
                /**
                 * 方法返回数据
                 */
                // 设置默认的异常处理
                ResultInfo resultInfo = new ResultInfo();
                resultInfo.setCode(500);
                resultInfo.setMsg("异常异常,请重试!");

                // 判断异常类型是否是自定义异常
                if (ex instanceof ParamsException) {
                    ParamsException p = (ParamsException) ex;
                    resultInfo.setCode(p.getCode());
                    resultInfo.setMsg(p.getMsg());

                } else if (ex instanceof AuthException) { // 认证异常
                    AuthException a = (AuthException) ex;
                    resultInfo.setCode(a.getCode());
                    resultInfo.setMsg(a.getMsg());
                }

                // 设置响应类型及编码格式(响应JSON格式的数据)
                response.setContentType("application/json;charset=UTF-8");
                // 得到字符输出流
                PrintWriter out = null;
                try {
                    // 得到输出流
                    out = response.getWriter();
                    // 将需要返回的对象转换成JOSN格式的字符
                    String json = JSON.toJSONString(resultInfo);
                    // 输出数据
                    out.write(json);

                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    // 如果对象不为空,则关闭
                    if (out != null) {
                        out.close();
                    }
                }

                return null;

            }
        }

        return modelAndView;
    }
}

非法请求拦截

对于后端菜单资源,这里要求用户必须进行登录来保护 web 资源的安全性,此时引入非法请求拦截功
能。

实现思路:

  • 判断用户是否是登录状态
    
  • 获取Cookie对象,解析用户ID的值
    
  • 如果用户ID不为空,且在数据库中存在对应的用户记录,表示请求合法
    
  • 否则,请求不合法,进行拦截,重定向到登录页面
    

定义拦截器
在新建 interceptors 包,创建 NoLoginInterceptor 类,并继承 HandlerInterceptorAdapter 适配器,
实现拦截器功能。

public class NoLoginInterceptor extends HandlerInterceptorAdapter {

    // 注入UserMapper
    @Resource
    private UserMapper userMapper;

    /**
     * 拦截用户是否是登录状态
     *  在目标方法(目标资源)执行前,执行的方法
     *
     *  方法返回布尔类型:
     *      如果返回true,表示目标方法可以被执行
     *      如果返回false,表示阻止目标方法执行
     *
     *  如果判断用户是否是登录状态:
     *      1. 判断cookie中是否存在用户信息(获取用户ID)
     *      2. 数据库中是否存在指定用户ID的值
     *
     *  如果用户是登录状态,则允许目标方法执行;如果用户是非登录状态,则抛出未登录异常 (在全局异常中做判断,如果是未登录异常,则跳转到登录页面)
     * @param request
     * @param response
     * @param handler
     * @return boolean
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        // 获取cookie中的用户ID
        Integer userId = LoginUserUtil.releaseUserIdFromCookie(request);
        // 判断用户ID是否为空,且数据库中存在该ID的用户记录
        if (null == userId || userMapper.selectByPrimaryKey(userId) == null) {
            // 抛出未登录异常
            throw new NoLoginException();
        }
        return true;
    }
}

写到最后,小冷一直在技术路上前行…你的关注,评论,收藏都是对我的支持。

昨天,删去;今天,争取;明天,努力。

点下方名片关注就可以获取资料。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小冷coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值