Spring:SpringMVC拦截器实现实训项目权限统一的校验,简化实训项目代码

本文介绍了如何使用SpringMVC拦截器进行实训项目的权限统一校验,简化代码。详细讨论了拦截器的配置、登录信息获取、解决管理员登录循环问题的方法,并给出代码重构的例子。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringMVC拦截器实现实训项目权限统一的校验

一、SpringMVC拦截器配置及类初始化
1.1 SpringMVC的配置文件dispatcher-servlet.xml

在dispatcher-servlet.xml中定义拦截器,如果是manage/a.do,则需要定义path为/manage/*,如果是manage/product/a.do,则需要定义path为/manage/**,再使用bean标签,让我们的拦截器指定到AuthorityInterceptor上

    <mvc:interceptors>
        <!--定义在这里的,所有都会拦截-->
        <mvc:interceptor>
            <mvc:mapping path="/manage/**"/>
            <bean class="cn.lnsf.controller.common.interceptor.AuthorityInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
1.2 AuthorityInterceptor.java

需要实现HandlerInterceptor接口,其中有3个方法,preHandle、postHandle、afterCompletion,当preHandle返回值为true后,便会进入到controller层,controller执行完成后就会进入到postHandle,再到afterCompletion

@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {
    //注重于preHandle,在用户进入controller的*.do之前判断其权限
    //return false的话就不会进入到controller里面
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        log.info("preHandle");
        //return false;
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        log.info("postHandle");
    }

    //在所有处理完成后调用的,如果我们项目不是前后端分离的,
    //需要返回一个视图的时候,就在视图呈现之后调用afterCompletion
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        log.info("afterCompletion");
    }
}

二、拦截器参数解析及登录信息获取,重置Response
2.1 AuthorityInterceptor.java
@Slf4j
public class AuthorityInterceptor implements HandlerInterceptor {
    //注重于preHandle,在用户进入controller的*.do之前判断其权限
    //return false的话就不会进入到controller里面
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        log.info("preHandle");
        //请求中Controller中的方法名
        HandlerMethod handlerMethod = (HandlerMethod)o;
        //解析HandlerMethod
        //例如:login
        String methodName = handlerMethod.getMethod().getName();
        //getSimpleName就是类名,name则还包含包名
        //例如:UserManageController
        String className = handlerMethod.getBean().getClass().getSimpleName();
        //解析参数,具体的参数key-value
        StringBuffer requestParamBuffer = new StringBuffer();
        Map paramMap = httpServletRequest.getParameterMap();
        //迭代
        Iterator it = paramMap.entrySet().iterator();
        while(it.hasNext()){
            //Map.Entry更方便输出
            Map.Entry entry = (Map.Entry) it.next();
            String mapKey = (String) entry.getKey();
            String mapValue = StringUtils.EMPTY;
            //request这个参数的map,里面的value返回的是一个string数组
            Object object = entry.getValue();
            if(object instanceof String[]){
                String[] strs = (String[])object;
                mapValue = Arrays.toString(strs);
            }
            //用于打印日志
            requestParamBuffer.append(mapKey).append("=").append(mapValue);
        }
        //判断
        User user = null;
        String loginToken = CookieUtil.readLoginToken(httpServletRequest);
        if(StringUtils.isNotEmpty(loginToken)){
            String userJsonStr = RedisShardedPoolUtil.get(loginToken);
            user = JsonUtil.string2Obj(userJsonStr,User.class);
        }
        if(user==null || (user.getRole().intValue()!= Const.Role.ROLE_ADMIN)){
            //返回false,即不会调用controller里面的方法
            //由于需要修改返回值,需要重置返回值后面使用getWriter才不会出现异常
            //这里相当于将response托管到拦截器当中,则需要重新设置属性
            httpServletResponse.reset();
            //设置编码
            httpServletResponse.setCharacterEncoding("UTF-8");
            //设置返回类型
            httpServletResponse.setContentType("application/json;charset=UTF-8");
            PrintWriter out = httpServletResponse.getWriter();
            if(user == null){
                out.println(JsonUtil.obj2String(ServerResponse.createByErrorMessage("拦截器拦截,用户未登录")));
            }else{
                out.println(JsonUtil.obj2String(ServerResponse.createByErrorMessage("拦截器拦截,用户权限不足")));
            }
            //将流清空,再关闭
            out.flush();
            out.close();
            //不进入controller
            return false;
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        log.info("postHandle");
    }

    //在所有处理完成后调用的,如果我们项目不是前后端分离的,
    //需要返回一个视图的时候,就在视图呈现之后调用afterCompletion
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        log.info("afterCompletion");
    }
}
2.2 测试结果

在这里插入图片描述

三、拦截管理员登录循环问题
3.1 拦截器产生的问题

如果login.do也是在manager/controller里面,但是用户在初次登录的时候拦截器的user本来就为null,所以请求不到login.do,这时候拦截器便让登录方法进入死循环

在这里插入图片描述
在这里插入图片描述

3.2 解决方法一,修改dispatcher-servlet.xml
    <mvc:interceptors>
        <!--定义在这里的,所有都会拦截-->
        <mvc:interceptor>
            <mvc:mapping path="/manage/**"/>
            <!--不拦截login.do-->
            <mvc:exclude-mapping path="/manage/user/login.do"/>
            <bean class="cn.lnsf.controller.common.interceptor.AuthorityInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>
3.3 解决方法二,修改拦截器(推荐)

由于可以通过在handler中获取到方法的名字以及类名,则通过判断是否为UserAdminController下的login的方法,如果是则不打印日志以及return true让方法进入controller

String methodName = handlerMethod.getMethod().getName();
String className = handlerMethod.getBean().getClass().getSimpleName();
//判断方法
if(StringUtils.equals(className,"UserAdminController")&&StringUtils.equals(methodName,"login")){
            log.info("拦截器拦截到请求,className:{},methodName:{}",className,methodName);
            //不打印参数,因为参数里面有账号密码
            return true;
        }

四、重构代码例子
4.1 管理员管理商品分类CategoryAdminController

初期代码中,如果管理员需要添加商品分类的话,/manage/category/下的add_category.do会从cookie中获取信息,进行判断,再进行权限判断

    //添加分类
    @RequestMapping("add_category.do")
    @ResponseBody
    public ServerResponse addCategory(HttpServletRequest httpServletRequest,String categoryName,@RequestParam(value = "parentId",defaultValue = "0") int parentId){
        String loginToken = CookieUtil.readLoginToken(httpServletRequest);
        if(StringUtils.isEmpty(loginToken)){
            return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息");
        }
        String userJsonStr = RedisShardedPoolUtil.get(loginToken);
        User user = JsonUtil.string2Obj(userJsonStr,User.class);
        if(user==null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录");
        }
        if(iUserService.checkAdminRole(user).isSuccess()){
            return iCategoryService.addCategory(categoryName,parentId);
        }
        return ServerResponse.createByErrorMessage("非管理员,无权限操作");
    }

    //修改分类名字
    @RequestMapping("update_category_name.do")
    @ResponseBody
    public ServerResponse setCategoryName(HttpServletRequest httpServletRequest,Integer categoryId,String categoryName){
        String loginToken = CookieUtil.readLoginToken(httpServletRequest);
        if(StringUtils.isEmpty(loginToken)){
            return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息");
        }
        String userJsonStr = RedisShardedPoolUtil.get(loginToken);
        User user = JsonUtil.string2Obj(userJsonStr,User.class);
        if(user==null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录");
        }
        if(iUserService.checkAdminRole(user).isSuccess()){
            return iCategoryService.updateCategoryName(categoryId,categoryName);
        }
        return ServerResponse.createByErrorMessage("非管理员,无权限操作");
    }

    //深度搜索
    @RequestMapping("deep_category.do")
    @ResponseBody
    public ServerResponse getCategoryAndDeepChildrenCategory(HttpServletRequest httpServletRequest, @RequestParam(value="categoryId",defaultValue = "0")int categoryId){
        String loginToken = CookieUtil.readLoginToken(httpServletRequest);
        if(StringUtils.isEmpty(loginToken)){
            return ServerResponse.createByErrorMessage("用户未登录,无法获取当前用户的信息");
        }
        String userJsonStr = RedisShardedPoolUtil.get(loginToken);
        User user = JsonUtil.string2Obj(userJsonStr,User.class);
        if(user==null){
            return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录");
        }
        if(iUserService.checkAdminRole(user).isSuccess()){
            //当前节点id和递归子节点id
            return iCategoryService.selectCategoryAndChildrenById(categoryId);
        }
        return ServerResponse.createByErrorMessage("非管理员,无权限操作");
    }

由于拦截器的作用包括了判断是否登录以及权限的问题,所以可以将添加、深搜、修改等代码改下如下,代码简洁明了

    //添加商品分类
	@RequestMapping("add_category.do")
    @ResponseBody
    public ServerResponse addCategory(HttpServletRequest httpServletRequest,String categoryName,@RequestParam(value = "parentId",defaultValue = "0") int parentId){
        return iCategoryService.addCategory(categoryName,parentId);
    }

    //修改分类名字
    @RequestMapping("update_category_name.do")
    @ResponseBody
    public ServerResponse setCategoryName(HttpServletRequest httpServletRequest,Integer categoryId,String categoryName){
        return iCategoryService.updateCategoryName(categoryId,categoryName);
    }

    //深度搜索
    @RequestMapping("deep_category.do")
    @ResponseBody
    public ServerResponse getCategoryAndDeepChildrenCategory(HttpServletRequest httpServletRequest, @RequestParam(value="categoryId",defaultValue = "0")int categoryId){
        return iCategoryService.selectCategoryAndChildrenById(categoryId);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值