SpringBoot中關於Shiro權限管理的整合使用


 shiro是一個輕量級的安全框架,包含用戶認證用戶授權

分析shiro的核心API:

Subject:用戶主體(把操作交給SecurityManager)

SecurityManager:安全管理器(管理Reaml)

Reaml:shiro連接數據的橋梁

 

 

Shiro的配置類:
 創建ShiroFilterFactoryBean;

 創建DefaultWebSecurityManager;

  創建Reaml(繼承AuthorizingReaml);

 

Shiro內置過濾器,可以實現權限相關的攔截器:

 常用的過濾器:

anno:無需認證(登陸)可以訪問

authc:必須認證才能訪問

user:如果使用rememberMe的功能可以直接訪問

perms:該資源必須得到資源權限可以訪問

role:該資源必須得到角色權限才能訪問

 

 

在整合Shiro的時候,我們先要確定一下我們的步驟:

1.加入Shiro的依賴包,實現自己的Realm類(通過繼承AuthorizingRealm類);

2.實現Shiro的配置類

3.實現前端的登錄界面以及Controller類

 

第一步:

在pom.xml中加入依賴包

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
 </dependency>

實現Realm類

package ariky.shiro.realm;
 
import java.util.HashSet;
import java.util.Set;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.subject.WebSubject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
/**
* @ClassName:
* @Description: Realm的配置
* @author fuweilian
* @date 2018-5-12 上午11:36:41
 */
public class MyShiroRealm extends AuthorizingRealm {
    //slf4j記錄日志,可以不使用
    private Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
 
    /**
     * 設置授權信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        logger.info("開始授權(doGetAuthorizationInfo)");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                .getSubject()).getServletRequest();//這個可以用來獲取在登錄的時候提交的其他額外的參數信息
        String username = (String) principals.getPrimaryPrincipal();//這里是寫的demo,后面在實際項目中葯通過這個登錄的賬號去獲取用戶的角色和權限,這里直接是寫死的
        //受理權限
        //角色
        Set<String> roles = new HashSet<String>();
        roles.add("role1");
        authorizationInfo.setRoles(roles);
        //權限
        Set<String> permissions = new HashSet<String>();
        permissions.add("user:list");
        //permissions.add("user:add");
        authorizationInfo.setStringPermissions(permissions);
        return authorizationInfo;
    }
 
    /**
     * 設置認證信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authenticationToken) throws AuthenticationException {
        logger.info("開始認證(doGetAuthenticationInfo)");
        //UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        HttpServletRequest request = (HttpServletRequest) ((WebSubject) SecurityUtils
                .getSubject()).getServletRequest();
        UsernamePasswordToken token = new UsernamePasswordToken (request.getParameter("userName"),request.getParameter("password"));
        //獲取用戶輸入的賬號
        String userName = (String)token.getPrincipal();
        //通過userName去數據庫中匹配用戶信息,通過查詢用戶的情況做下面的處理
        //這里暫時就直接寫死,根據登錄用戶賬號的情況做處理
        logger.info("賬號:"+userName);
        if("passwordError".equals(userName)){//密碼錯誤
            throw new IncorrectCredentialsException(); 
        }else if("lockAccount".equals(userName)){// 用戶鎖定
            throw new LockedAccountException(); 
        }else{
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    userName, //用戶名
                    "123456", //密碼,寫死
                    ByteSource.Util.bytes(userName+"salt"),//salt=username+salt
                    getName()  //realm name
            );
            return authenticationInfo;
        }
    }
    
}

第二步 實現Shiro的配置類:

package ariky.shiro.configuration;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import ariky.shiro.realm.MyShiroRealm;
 
/**
* @ClassName: ShiroConfiguration 
* @Description: shiro的配置類 
* @author fuweilian
* @date 2018-5-12 上午11:05:09
 */
@Configuration
public class ShiroConfiguration {
    private static Logger logger = LoggerFactory.getLogger(ShiroConfiguration.class);
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        logger.info("進入shiroFilter......");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //設置不需要攔截的路徑
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //按順序依次判斷
        filterChainDefinitionMap.put("/static/**", "anon");
        //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
        /************************************初始化所有的權限信息開始******************************************/
        //這里,如果以后再項目中使用的話,直接從數據庫中查詢
        filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
        //filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
        /***************************************初始化所有的權限信息開始結束*********************************************/
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授權界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    
    @Bean
    public MyShiroRealm myShiroRealm(){
         MyShiroRealm myShiroRealm = new MyShiroRealm();
         //后面這里可以設置緩存的機制
         return myShiroRealm;
    } 
    
    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        return securityManager;
    }
    
    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    
}

第三步:實現Controoler類,這里寫倆個類,一個是登錄信息的LoginController處理類,一個是測試權限用的UserController

1.LoginController.java

package ariky.controller;
 
import java.util.Map;
 
import javax.servlet.http.HttpServletRequest;
 
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
 
/**
* @ClassName: LoginController 
* @Description: 登錄控制的controller
* @author fuweilian
* @date 2018-5-12 下午01:15:46
 */
@RequestMapping
@Controller
public class LoginController {
    private Logger logger = LoggerFactory.getLogger(LoginController.class);
    
    @RequestMapping(value="/login",method=RequestMethod.GET)
    public String getLogin(){
        logger.info("進入login頁面");
        return "login";
    }
    
    @RequestMapping(value="/login",method=RequestMethod.POST)
    public String doLogin(HttpServletRequest req,Map<String, Object> model){
        logger.info("進入登錄處理");
        String exceptionClassName = (String) req.getAttribute("shiroLoginFailure");
        logger.info("exceptionClassName:"+exceptionClassName);
        String error = null;
        if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯誤";
        } else if (IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
            error = "用戶名/密碼錯誤";
        }else if(LockedAccountException.class.getName().equals(exceptionClassName)){ 
            error = "用戶已鎖定或已刪除";
        }else if (exceptionClassName != null) {
            error = "其他錯誤:" + exceptionClassName;
        }
        if(SecurityUtils.getSubject().isAuthenticated()){//沒有錯誤,但是已經登錄了,就直接跳轉到welcom頁面
            model.put("name", req.getParameter("userName"));
            return "index";
        }else{//有錯誤的
            model.put("error", error);
            return "login";
        }
    }
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
}

2.UserController.java

package ariky.controller;
 
import java.util.ArrayList;
import java.util.List;
 
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
* @ClassName: UserController 
* @Description: 用戶處理Controller
* @author fuweilian
* @date 2018-5-12 下午03:11:06
 */
@Controller
@RequestMapping("/user")
public class UserController {
    Logger logger = LoggerFactory.getLogger(UserController.class);
    @RequiresPermissions("user:list")//這個是配置是否有該權限的,如果是按上面的寫法,這個是有權限的
    @RequestMapping(value="/list",method=RequestMethod.GET)
    public String getList(){
        logger.info("進入用戶列表");
        return "user/list";
    }
    @RequiresPermissions(value={"user:add"})//這個是沒有權限的
    @RequestMapping(value="/add",method=RequestMethod.GET)
    public String getAdd(){
        logger.info("進入新增用戶界面");
        return "user/add";
    }
    
}

前端界面:有5個界面 (login.jsp,index.jsp,list.jsp,add.jsp,403.jsp)

目錄結構為:

1.login.jsp

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Login</title>
</head>
 
<body>
    <h1>登錄頁面----${error}</h1>
    <form:form action="${pageContext.request.contextPath }/login"
         method="post">
        用戶名:<input type="text" name="userName">
        <br />
        密碼:<input type="passwordParam" name="password"/>
        <input type="submit" value="提交"/>
    </form:form>
</body>
</html>

2.index.jsp

<%@ page language="java" pageEncoding="UTF-8"%>
 <%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>第一個例子</title>
<script src="${pageContext.request.contextPath }/webjars/jquery/2.1.4/jquery.js"></script>
<script src="${pageContext.request.contextPath }/webjarslocator/jquery/jquery.js"></script>
</head>
 
<body>
    <h1>${name}:你好,歡迎訪問該網頁</h1>
        <shiro:hasPermission name="user:list"><!-- 這個a標簽是可以看見的 -->
            <a href="${pageContext.request.contextPath }/user/list" target="_blank">跳轉到用戶列表(有權限)</a>
        </shiro:hasPermission>
    <br/>
        <shiro:hasPermission name="user:add"><!-- 這個a標簽是看不見的 -->
            <a href="${pageContext.request.contextPath }/user/add" target="_blank">跳轉到新增用戶列表(無權限)</a>
        </shiro:hasPermission>
</body>
</html>

3.list.jsp和add.jsp以及403.jsp都差不多一樣,這里就寫一個,這里只是demo所用,在實際項目中,要以實際項目為准

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>userList</title>
</head>
 
<body>
    <h1>用戶列表信息</h1>
</body>
</html>

上面就是全部代碼了,如果啟動成功,進入login登錄界面就可以測試一下shiro的權限認證了。上面的代碼都是寫死的,如果想要實現動態的權限管理和用戶的權限管理的話,還要做一些其他處理,用戶的動態權限這個只要在自己的ShiroRealm類里面授權的時候做一下查詢數據庫,動態的授權和角色就行。關於動態的權限管理的話,下面的方式可以實現,在修改完權限數據后,更新一下shiro里面的配置就行,具體看下面的代碼,這里是demo,不是實際項目,在實際項目中最好不要把邏輯寫在Controller里面

package ariky.shiro.controller;
 
import java.util.LinkedHashMap;
import java.util.Map;
 
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
 
/**
* @ClassName: PermssionController 
* @Description: 權限操作的controller 
* @author fuweilian
* @date 2018-5-12 下午04:59:15
 */
@Controller
@RequestMapping("permssion")
public class PermssionController {
    
     @Autowired
     ShiroFilterFactoryBean shiroFilterFactoryBean;
     
     /**
     * @Title: updatePermssion 
     * @author: fuweilian
     * @Description: 這里暫時直接寫在controller里面,,不按規則寫了,,到時候在項目中使用的時候,才寫
     * @return  參數說明 
     * @return Object    返回類型 
     * @throws
      */
    @RequestMapping("/updatePermssion")
    @ResponseBody
    public Object updatePermssion(){
        synchronized (shiroFilterFactoryBean){
            AbstractShiroFilter shiroFilter = null;
            try {
                shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean
                        .getObject();
                PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
                    .getFilterChainResolver();
                DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver
                    .getFilterChainManager();
                // 清空老的權限控制
                manager.getFilterChains().clear();
                shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();
                //后面這個可以直接從數據庫里面獲取
                Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
                //按順序依次判斷
                filterChainDefinitionMap.put("/static/**", "anon");
                //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了
                filterChainDefinitionMap.put("/logout", "logout");
                //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
                /************************************初始化所有的權限信息開始******************************************/
                //這里,如果以后再項目中使用的話,直接從數據庫中查詢
                filterChainDefinitionMap.put("/user/list", "authc,perms[user:list]");
                filterChainDefinitionMap.put("/user/add", "authc,perms[user:add]");
                /***************************************初始化所有的權限信息開始結束*********************************************/
                filterChainDefinitionMap.put("/**", "authc");
                //
                shiroFilterFactoryBean.setLoginUrl("/login");
                // 登錄成功后要跳轉的鏈接
                shiroFilterFactoryBean.setSuccessUrl("/index");
                //未授權界面
                shiroFilterFactoryBean.setUnauthorizedUrl("/error/403");
                shiroFilterFactoryBean
                    .setFilterChainDefinitionMap(filterChainDefinitionMap);
                // 重新構建生成
                Map<String, String> chains = shiroFilterFactoryBean
                        .getFilterChainDefinitionMap();
                for (Map.Entry<String, String> entry : chains.entrySet()) {
                    String url = entry.getKey();
                    String chainDefinition = entry.getValue().trim()
                            .replace(" ", "");
                    manager.createChain(url, chainDefinition);
                }
                return "更新權限成功";  
            } catch (Exception e) {
                throw new RuntimeException(
                        "更新shiro權限出現錯誤!");
            }
        }
    }
 
}

下面是mysql庫的表結構

/*
Navicat MySQL Data Transfer
Source Server         : arikyDB
Source Server Version : 50721
Source Host           : 47.106.95.168:3306
Source Database       : ariky
Target Server Type    : MYSQL
Target Server Version : 50721
File Encoding         : 65001
Date: 2018-05-14 16:05:51
*/
 
SET FOREIGN_KEY_CHECKS=0;
 
-- ----------------------------
-- Table structure for common_permssion
-- ----------------------------
DROP TABLE IF EXISTS `common_permssion`;
CREATE TABLE `common_permssion` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `NAME` varchar(255) DEFAULT NULL COMMENT '權限名稱',
  `TYPE` varchar(255) DEFAULT NULL COMMENT '類型按鈕(button)或者菜單(menu) ',
  `PARENT_ID` int(11) DEFAULT NULL COMMENT '上級ID',
  `PARENT_IDS` varchar(255) DEFAULT NULL COMMENT '上級PIDs',
  `URL` varchar(255) DEFAULT NULL COMMENT '訪問路徑',
  `ICONCLS` varchar(255) DEFAULT NULL COMMENT '圖標(可以不要)',
  `PERMISSION` varchar(255) DEFAULT NULL COMMENT '權限(如user:list)',
  `ORDER_NUM` int(11) DEFAULT NULL COMMENT '排序',
  `REMARK` varchar(255) DEFAULT NULL COMMENT '備注',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=41 DEFAULT CHARSET=utf8 COMMENT='該表用來存儲資源權限信息';
 
-- ----------------------------
-- Table structure for common_role
-- ----------------------------
DROP TABLE IF EXISTS `common_role`;
CREATE TABLE `common_role` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',
  `LABEL_ID` varchar(255) DEFAULT NULL COMMENT '標簽Id',
  `NAME` varchar(255) DEFAULT NULL COMMENT '角色名稱',
  `ROLE` varchar(255) DEFAULT NULL,
  `DESCRIPTION` varchar(255) DEFAULT NULL,
  `IS_SHOW` int(11) DEFAULT '1' COMMENT '判斷該角色是否在使用(1:使用,2:禁用)',
  `IS_HANDLER` int(2) DEFAULT NULL COMMENT '判斷是什么角色(1:后台角色,2:商家管理員角色,3:商家添加用戶角色,4:游客角色)',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8 COMMENT='角色表';
 
-- ----------------------------
-- Table structure for common_role_permssion
-- ----------------------------
DROP TABLE IF EXISTS `common_role_permssion`;
CREATE TABLE `common_role_permssion` (
  `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵Id',
  `ROLE_ID` int(11) DEFAULT NULL COMMENT '角色Id',
  `RESOURCE_ID` int(11) DEFAULT NULL COMMENT '資源(權限)Id',
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=493 DEFAULT CHARSET=utf8 COMMENT='角色資源權限表中間表';

 


免責聲明!

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



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