zoukankan      html  css  js  c++  java
  • Shiro 权限校验不通过时,区分GET和POST请求正确响应对应的方式

    引入:https://blog.csdn.net/catoop/article/details/69210140

    本文基于Shiro权限注解方式来控制Controller方法是否能够访问。 
    例如使用到注解: 
    @RequiresPermissions 来控制是否有对应权限才可以访问 
    @RequiresUser 来控制是否存在用户登录状态才可以访问

    想了解Shiro是如何通过注解来控制权限的,可以查看源码 AopAllianceAnnotationsAuthorizingMethodInterceptor ,其构造方法中添加了几个对应的权限注解方法拦截器(这里不做详细阐述)。

    用户在请求使用这些注解方式控制的方法时,如果没有通过权限校验。Shiro 会抛出如下两组类型的异常。

    登录认证类异常 UnauthenticatedException.class, AuthenticationException.class 
    权限认证类异常 UnauthorizedException.class, AuthorizationException.class 
    (每个具体的异常对应哪个注解,大家查看源码了解一下)

    言归正传,直接上代码,通过代码来说明本文目的 “做Get和Post请求的时候,如果请求的URL是被注解权限控制的,在没有权限或者登陆失效的情况下,如果以正确方式的返回结果(如果用户没有登录,大多数都是直接跳转到登录页面了)”。

    由于项目前端框架设定,如新增一个用户,先跳转新增用户页面,然后去保存用户信息,跳转新增用户页面是get请求,保存用户信息是Post请求。

    实现如下:

    通过一个 BaseController 来统一处理,然后被其他 Controller 继承即可,对于JSON和页面跳转,我们只需要做一个Ajax判断处理即可。

    代码如下:

    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Arrays;
    import java.util.Map.Entry;
    import java.util.stream.Collectors;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.beanutils.BeanMap;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authz.AuthorizationException;
    import org.apache.shiro.authz.UnauthenticatedException;
    import org.apache.shiro.authz.UnauthorizedException;
    import org.apache.shiro.subject.Subject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    
    import com.zfull.commons.result.QueryResult;
    import com.zfull.commons.web.vo.ReturnJsonVO;
    import com.zfull.commons.web.vo.ShiroAccountVO;
    import com.zfull.facade.authority.dto.BzMenuDTO;
    import com.zfull.facade.authority.query.BzMenuQuery;
    import com.zfull.facade.authority.service.BzMenuService;
    
    import net.sf.json.JSONArray;
    
    /**
     * 基础Controller
     * @ClassName: BaseController 
     * @Description: TODO
     * @author OnlyMate
     * @Date 2018年4月11日 下午2:30:00  
     *
     */
    public class BaseController {
        protected Logger log = LoggerFactory.getLogger(this.getClass());
    
        protected final static String REDIRECT_LOGIN = "redirect:/login";
        
        @Autowired
        private BzMenuService menuService;
    
        // 右侧功能菜单
        public String menuRight(String urlstr) {
            String parMenuId = menuService.findMenuByAction(urlstr).getResults().get(0).getMenuId();
            ShiroAccountVO currShiroUser = getCurrentUser();
            String[] roleIds = currShiroUser.getRoleIds().split(",");// 当前登录用户所属角色
            // 右侧菜单
            BzMenuQuery menuQuery = new BzMenuQuery ();
            menuQuery.setParentId(parMenuId);
            menuQuery.setRoleIds(Arrays.asList(roleIds).stream().map(s -> Integer.parseInt(s.trim())).collect(Collectors.toList()));
            QueryResult<BzMenuDTO> source = menuService.findMenuList(menuQuery);
            StringBuilder htmls = new StringBuilder();
            String menuids = "";
            if (source != null && source.getResults().size() > 0) {
                for (BzMenuDTO entity : source.getResults()) {
                    if (menuids.indexOf(entity.getMenuId()) > -1) {
                        continue;
                    }
                    menuids += entity.getMenuId() + ",";
                    if (entity.getFunction().contains("#")) {
                        /*htmls.append(
                                " <a href='" + entity.getMenuengname() + "'data-backdrop='static' data-toggle='modal'>");
                        htmls.append("<i class='" + entity.getIcon() + "'></i> ");
                        htmls.append(entity.getMenuname() + "</a>");*/
                    }else {
                        htmls.append(" <button class='btn' onclick='" + entity.getFunction() + "' >");
                        htmls.append("<i class='"+entity.getIcon()+"'></i>");
                        htmls.append("<span>"+entity.getMenuName() + "</span></button>");
                    }
                }
            }
            htmls.append(" <input type='hidden' id='chkAction' name='chkAction' value='" + urlstr + "' />");
            return htmls.toString();
        }
    
        public ShiroAccountVO getCurrentUser() {
            Subject subject = SecurityUtils.getSubject();
            return (ShiroAccountVO) subject.getPrincipal();
        }
    
        public String searchParams(Object obj) {
            BeanMap map = new BeanMap(obj);
            StringBuilder searchParams = new StringBuilder();
            for (Entry<Object, Object> entry : map.entrySet()) {
                if (!"class".equals(entry.getKey()) && !"pageSize".equals(entry.getKey()) && !"flag".equals(entry.getKey())
                        && !"pageNum".equals(entry.getKey()) && entry.getValue() != null) {
                    searchParams.append(entry.getKey());
                    searchParams.append("=");
                    searchParams.append(entry.getValue());
                    searchParams.append("&");
                }
            }
            return searchParams.toString();
        }
        /*********************** 以下是重点 *************************/
        /**
         * 登录认证异常(这个异常基本没有用,一般登录那里做了处理)
         * @Title: authenticationException 
         * @Description: TODO
         * @Date 2018年4月11日 下午2:19:06 
         * @author OnlyMate
         * @param request
         * @param response
         * @return
         */
        @ExceptionHandler({ UnauthenticatedException.class, AuthenticationException.class })
        public String authenticationException(HttpServletRequest request, HttpServletResponse response) {
            if (isAjaxRequest(request)) {
                // 输出JSON
                ReturnJsonVO returnJson = new ReturnJsonVO();
                returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); // 提交失败 1
                String message = "当前登录用户无该权限";
                returnJson.setMessage(message);
                writeJson(returnJson, response);
                return null;
            } else {
                return "redirect:/login";
            }
        }
    
        /**
         * 权限异常
         * @Title: authorizationException 
         * @Description: TODO
         * @Date 2018年4月11日 下午2:19:18 
         * @author OnlyMate
         * @param request
         * @param response
         * @return
         */
        @ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class })
        public String authorizationException(HttpServletRequest request, HttpServletResponse response) {
            if (isAjaxRequest(request)) {
                // 输出JSON
                ReturnJsonVO returnJson = new ReturnJsonVO();
                returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); // 提交失败 1
                String message = "当前登录用户无该权限";
                returnJson.setMessage(message);
                writeJson(returnJson, response);
                return null;
            } else {
                return "redirect:/unauthor";
            }
        }
    
        /**
         * 输出JSON
         * @Title: writeJson 
         * @Description: TODO
         * @Date 2018年4月11日 下午2:18:10 
         * @author OnlyMate
         * @param returnJson
         * @param response
         */
        private void writeJson(ReturnJsonVO returnJson, HttpServletResponse response) {
            PrintWriter out = null;
            try {
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json; charset=utf-8");
                out = response.getWriter();
                out.write(JSONArray.fromObject(returnJson).toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (out != null) {
                    out.close();
                }
            }
        }
    
        /**
         * 是否是Ajax请求
         * @Title: isAjaxRequest 
         * @Description: TODO
         * @Date 2018年4月11日 下午2:19:31 
         * @author OnlyMate
         * @param request
         * @return
         */
        public static boolean isAjaxRequest(HttpServletRequest request) {
            String requestedWith = request.getHeader("x-requested-with");
            if (requestedWith != null && requestedWith.equalsIgnoreCase("XMLHttpRequest")) {
                return true;
            } else {
                return false;
            }
        }
    }

    下面是一个普通的 Controller,继承了BaseController

    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.Map;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.apache.commons.lang.StringUtils;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.fastjson.JSON;
    import com.google.common.collect.Maps;
    import com.zfull.commons.enums.basic.RoleLevelEnum;
    import com.zfull.commons.result.QueryResult;
    import com.zfull.commons.result.Result;
    import com.zfull.commons.result.SingleResult;
    import com.zfull.commons.security.CipherTools;
    import com.zfull.commons.utils.DateUtil;
    import com.zfull.commons.web.utils.JsonMapper;
    import com.zfull.commons.web.vo.ReturnJsonVO;
    import com.zfull.commons.web.vo.ShiroAccountVO;
    import com.zfull.facade.authority.dto.BzOperToRole;
    import com.zfull.facade.authority.dto.BzOperatorDTO;
    import com.zfull.facade.authority.dto.BzRoleDTO;
    import com.zfull.facade.authority.query.BzOperatorQuery;
    import com.zfull.facade.authority.query.BzRoleQuery;
    import com.zfull.facade.authority.service.BzOperatorMchtService;
    import com.zfull.facade.authority.service.BzRoleService;
    import com.zfull.facade.authority.vo.BzOperatorVO;
    import com.zfull.web.common.BaseController;
    
    import net.sf.json.JSONArray;
    
    @Controller
    @RequestMapping(value=BzOperatorMchtController.PARENT_URL)
    public class BzOperatorMchtController extends BaseController{
        protected final static String PARENT_URL = "/permission/operatormcht";
        private static JsonMapper mapper = JsonMapper.nonDefaultMapper();
        
        @Autowired
        private BzOperatorMchtService operatorMchtService;
        @Autowired
        private BzRoleService roleService;
        
        /**
         * 用户列表
         * @Title: index 
         * @Description: TODO
         * @Date: 2018年3月26日 下午2:34:49 
         * @author: OnlyMate
         * @throws: 
         * @param request
         * @param query
         * @return
         */
        @RequiresPermissions(value="operatormcht.index")
        @RequestMapping(method = RequestMethod.GET)
        public String index(HttpServletRequest request, Model model, BzOperatorQuery query) {
            ReturnJsonVO returnJson = new ReturnJsonVO();
            returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); //提交失败 1
            String message = "查询用户首页出错";
            // 获取当前操作员信息
            ShiroAccountVO currShiroUser = getCurrentUser();
            
            QueryResult<BzOperatorVO> result = new QueryResult<BzOperatorVO>();
            try {
                // TODO 按照登录用户去筛选数据,查询当前的商户信息
                
                result = operatorMchtService.queryOperatorList(query);
                if(result.isSuccess()) {
                    message = "查询用户首页成功";
                    returnJson.setStatus("0");
                    returnJson.setData(JSON.toJSONString(result));
                }else {
                    message = "查询用户首页失败";
                }
            } catch (Exception e) {
                message = "查询用户首页出错";
                log.error(message);
                e.printStackTrace();
            }finally {
                returnJson.setMessage(message);
                log.info("系统日志:登录名={},操作员={},ip={},日期={},操作{}的{}方法-{}",
                        currShiroUser.getOperId(),currShiroUser.getOperName(),currShiroUser.getLoginIP(),
                  DateUtil.currentDatetime(),
    "BzOperatorMchtController","index",message); } model.addAttribute("roleinfos", roleService.findRoleList(new BzRoleQuery())); model.addAttribute("source", result); model.addAttribute("query", query); model.addAttribute("menuRight", menuRight(PARENT_URL)); model.addAttribute("searchParams", searchParams(query)); model.addAttribute("currentOperId", currShiroUser.getOperId()); return PARENT_URL + "/index"; } /** * 用户详情 * @Title: detail * @Description: TODO * @Date: 2018年3月26日 下午2:35:01 * @author: OnlyMate * @throws: * @param request * @param operId * @return */ @ResponseBody @RequiresPermissions(value="operatormcht.detail") @RequestMapping(value="/detail", method = RequestMethod.POST) public ReturnJsonVO detail(HttpServletRequest request, String operId) { ReturnJsonVO returnJson = new ReturnJsonVO(); returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); //提交失败 1 String message = "查询用户详情出错"; // 获取当前操作员信息 // ShiroAccountVO currShiroUser = getCurrentUser(); try { if(StringUtils.isBlank(operId)) { message = "传入参数有误"; returnJson.setMessage(message); return returnJson; } SingleResult<BzOperatorDTO> result = operatorMchtService.findByOperId(operId); if(result.isSuccess()) { returnJson.setStatus("0"); returnJson.setData(JSON.toJSONString(result.getResult())); message = "查询用户详情成功"; }else { message = "查询用户详情失败"; } } catch (Exception e) { message = "查询用户详情出错"; log.error(message); e.printStackTrace(); }finally { returnJson.setMessage(message); } return returnJson; } /** * 跳转新增用户界面 * @Title: addView * @Description: TODO * @Date: 2018年4月2日 上午1:45:45 * @author: OnlyMate * @throws: * @param model * @return */ @RequiresPermissions(value = "operatormcht.addView") @RequestMapping(value = "/addView", method = RequestMethod.GET) public String addView(Model model) { ReturnJsonVO returnJson = new ReturnJsonVO(); returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); // 提交失败 1 String message = "跳转新增用户页面出错"; try { //TODO 查询机构和商户信息 message = "跳转新增用户页面成功"; returnJson.setStatus("0"); returnJson.setData(JSON.toJSONString("")); } catch (Exception e) { message = "跳转新增用户页面出错"; log.error(message); e.printStackTrace(); } finally { returnJson.setMessage(message); } return PARENT_URL + "/add"; } /** * 保存用户 * @Title: add * @Description: TODO * @Date: 2018年3月26日 下午2:35:45 * @author: OnlyMate * @throws: * @param request * @param dto * @return */ @ResponseBody @RequiresPermissions(value="operatormcht.add") @RequestMapping(value="/add", method = RequestMethod.POST) public String add(HttpServletRequest request, BzOperatorDTO dto) { ReturnJsonVO returnJson = new ReturnJsonVO(); returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); //提交失败 1 String message = "用户新增出错"; // 获取当前操作员信息 ShiroAccountVO currShiroUser = getCurrentUser(); BzOperatorQuery query = new BzOperatorQuery(); boolean flag = Boolean.TRUE; try { if(StringUtils.isNotBlank(dto.getOperId()) && StringUtils.isNotBlank(dto.getBindPhone()) && StringUtils.isNotBlank(dto.getBindEmail())) { query.setLoginName(dto.getOperId()); if(flag && !checkLoginName(query)) { flag = Boolean.FALSE; message = "用户名已存在"; } query.setLoginName(dto.getBindPhone()); if(flag && !checkLoginName(query)){ flag = Boolean.FALSE; message = "绑定手机号已存在"; } query.setLoginName(dto.getBindEmail()); if(flag && !checkLoginName(query)) { flag = Boolean.FALSE; message = "绑定邮箱号已存在"; } if(flag) { dto.setPasswd("a94d5cd0079cfc8db030e1107de1addd1903a01b"); dto.setOnlineFlag("OFFLINE"); dto.setInitPasswd("INIT"); dto.setCreateFlag("MANUAL"); dto.setLoginCount(0); dto.setLastTime(new Date()); Result result = operatorMchtService.insertOperator(dto); if(result.isSuccess()) { message = "用户新增成功"; returnJson.setStatus("0"); }else { message = "用户新增失败"; } } }else { message = "传入参数有误"; } } catch (Exception e) { message = "用户新增出错"; log.error(message); e.printStackTrace(); }finally { returnJson.setMessage(message); log.info("系统日志:登录名={},操作员={},ip={},日期={},操作{}的{}方法-{}", currShiroUser.getOperId(),currShiroUser.getOperName(),currShiroUser.getLoginIP(),
                  DateUtil.currentDatetime(),
    "BzOperatorMchtController","add",message); } return JSONArray.fromObject(returnJson).toString(); } /** * 跳转用户编辑页面 * @Title: editView * @Description: TODO * @Date: 2018年4月2日 上午10:44:10 * @author: OnlyMate * @throws: * @param model * @param query * @return */ @RequiresPermissions(value = "operatormcht.editView") @RequestMapping(value = "/editView", method = RequestMethod.GET) public String editView(Model model, BzOperatorQuery query) { ReturnJsonVO returnJson = new ReturnJsonVO(); returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); // 提交失败 1 String message = "跳转编辑用户页面出错"; BzOperatorDTO oper = new BzOperatorDTO(); try { if (StringUtils.isBlank(query.getOperId())) { message = "传入参数有误"; }else { oper = operatorMchtService.findByOperId(query.getOperId()).getResult(); message = "跳转编辑用户页面成功"; returnJson.setStatus("0"); returnJson.setData(JSON.toJSONString(oper)); } } catch (Exception e) { message = "跳转编辑用户页面出错"; log.error(message); e.printStackTrace(); } finally { returnJson.setMessage(message); } model.addAttribute("oper", oper); return PARENT_URL + "/edit"; } /** * 更新用户 * @Title: edit * @Description: TODO * @Date: 2018年3月26日 下午2:36:02 * @author: OnlyMate * @throws: * @param request * @param dto * @return */ @ResponseBody @RequiresPermissions(value="operatormcht.edit") @RequestMapping(value="/edit", method = RequestMethod.POST) public String edit(HttpServletRequest request, BzOperatorDTO dto) { ReturnJsonVO returnJson = new ReturnJsonVO(); returnJson.setStatus(ReturnJsonVO.SUBMIT_FAILURE); //提交失败 1 String message = "用户更新出错"; // 获取当前操作员信息 ShiroAccountVO currShiroUser = getCurrentUser(); BzOperatorQuery query = new BzOperatorQuery(); boolean flag = Boolean.TRUE; try { if(StringUtils.isNotBlank(dto.getOperId()) && StringUtils.isNotBlank(dto.getBindPhone()) && StringUtils.isNotBlank(dto.getBindEmail())) { query.setOperId(dto.getOperId()); query.setLoginName(dto.getOperId()); if(flag && !checkLoginName(query)) { flag = Boolean.FALSE; message = "用户名已存在"; } query.setLoginName(dto.getBindPhone()); if(flag && !checkLoginName(query)){ flag = Boolean.FALSE; message = "绑定手机号已存在"; } query.setLoginName(dto.getBindEmail()); if(flag && !checkLoginName(query)) { flag = Boolean.FALSE; message = "绑定邮箱号已存在"; } if(flag) { BzOperatorDTO oldOperator = operatorMchtService.findByOperId(dto.getOperId()).getResult(); dto.setOnlineFlag(oldOperator.getOnlineFlag()); dto.setInitPasswd(oldOperator.getInitPasswd()); dto.setCreateFlag(oldOperator.getCreateFlag()); dto.setLoginCount(oldOperator.getLoginCount()); dto.setLastTime(new Date()); Result result = operatorMchtService.updateOperator(dto); if(result.isSuccess()) { message = "用户更新成功"; returnJson.setStatus("0"); }else { message = "用户更新失败"; } } }else { message = "传入参数有误"; } } catch (Exception e) { message = "用户更新出错"; log.error(message); e.printStackTrace(); }finally { returnJson.setMessage(message); log.info("系统日志:登录名={},操作员={},ip={},日期={},操作{}的{}方法-{}", currShiroUser.getOperId(),currShiroUser.getOperName(),currShiroUser.getLoginIP(),
                    DateUtil.currentDatetime(),
    "BzOperatorMchtController","edit",message); } return JSONArray.fromObject(returnJson).toString(); } }

    未授权路径

    /**
         * 未授权页面
         * @Title: unauthor 
         * @Description: TODO
         * @Date 2018年4月11日 上午12:19:37 
         * @author OnlyMate
         * @param request
         * @param response
         * @param model
         * @return
         */
        @RequestMapping(value = "/unauthor", method = RequestMethod.GET)
        public String unauthor(HttpServletRequest request, HttpServletResponse response, Model model){
            return "/unauthor";
        }

     未授权页面

    <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
    <c:set var="ctx" value="${pageContext.request.contextPath}" />
    <!DOCTYPE HTML>
        <head>
            <link rel="stylesheet" href="${ctx}/static/lib/jquery-ztree/css/zTreeStyle.css" type="text/css" />
            <link rel="stylesheet" href="${ctx}/static/lib/bootstrap/css/bootstrap.css">
            <link rel="stylesheet" href="${ctx}/static/css/reset.css">
            <link rel="stylesheet"
                href="${ctx}/static/lib/jquery.mCustomScrollbar/jquery.mCustomScrollbar.css">
            <link rel="stylesheet" href="${ctx}/static/css/index.css">
            <link rel="stylesheet" href="${ctx}/static/css/main.css">
            <link rel="stylesheet" href="${ctx}/static/css/style.css">
            <link rel="stylesheet" href="${ctx}/static/img/splashy/splashy.css">
            <link href="${ctx}/static/lib/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css"/>
        </head>
        <body>
            <div class="main_con">
                <div class="btn-actions">
                    <span style="color: red;font-size: 20px;">当前登录用户无该权限</span>
                </div>
            </div>
        </body>
    </html>

    当我们使用 get方式去请求/permission/operatormcht/addView时,如果用户没有授权,则重定向"redirect:/unauthor"到unauthor.jsp页面。 

    效果如下:

    当我们使用  post方式去请求/permission/operatormcht/edit时,如果用户没有授权,则会返回没有授权的JSON结果,而不是页面。

     效果如下:

    这样解决了,在shrio权限校验中,区分Get和Post请求以正确的方式去返回对应的信息,get返回没有授权的页面,post返回没有授权的Json信息。

  • 相关阅读:
    【LeetCode每天一题】Pascal's Triangle(杨辉三角)
    【Redis】持久化
    【LeetCode每天一题】Swap Nodes in Pairs
    【LeetCode每天一题】Reverse String
    [bzoj2152]聪聪可可
    [bzoj3572][Hnoi2014]世界树
    Codeforces Round#409/VK-Cup 2017 Round2
    Educational Codeforces Round#19
    [bzoj4813][Cqoi2017]小Q的棋盘
    [bzoj4236]JOIOJI
  • 原文地址:https://www.cnblogs.com/onlymate/p/8795751.html
Copyright © 2011-2022 走看看