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信息。

posted @ 2018-04-11 14:54  路途寻码人  阅读(5781)  评论(0编辑  收藏  举报