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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM