Shiro權限管理框架(一):Shiro的基本使用

核心概念

Apache Shiro是一個強大且易用的Java安全框架,執行身份驗證、受權、密碼和會話管理。使用Shiro的易於理解的API,您能夠快速、輕鬆地得到任何應用程序,從最小的移動應用程序到最大的網絡和企業應用程序。html

上面這段話來自百度百科,是否是很是官方,好像說的很明白可是又好像什麼都沒說的樣子,究竟是個啥呀。想要快速理解並使用Shiro先要從最重要的三大概念入手。前端

  1. Subject:大白話來說就是用戶(固然並不必定是用戶,也能夠指和當前應用交互的任何對象),咱們在進行受權鑑權的全部操做都是圍繞Subject(用戶)展開的,在當前應用的任何地方均可以經過SecurityUtils的靜態方法getSubject()輕鬆的拿到當前認證(登陸)的用戶。
  2. SecurityManager:安全管理器,Shiro中最核心的組件,它管理着當前應用中全部的安全操做,包括Subject(用戶),咱們圍繞Subject展開的全部操做都須要與SecurityManager進行交互。能夠理解爲SpringMVC中的前端控制器。
  3. Realms:字面意思爲領域,Shiro在進行權限操做時,須要從Realms中獲取安全數據,也就是用戶以及用戶的角色和權限。配置Shiro,咱們至少須要配置一個Realms,用於用戶的認證和受權。一般咱們的角色及權限信息都是存放在數據庫中,因此Realms也能夠算是一個權限相關的Dao層,SecurityManager在進行鑑權時會從Realms中獲取權限信息。

這三個基本的概念簡答理解後就能夠開始配置和使用Shiro了,其實Shiro最基本的使用很是簡單,加入依賴後只須要配置兩個Bean,再繼承一個抽象類實現兩個方法便可。java

首發地址:https://www.guitu18.com/post/2019/07/26/43.htmlgit


基本使用

引入一個依賴

新建一個基於Springboot的Web項目,引入Shiro依賴。github

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

配置兩個Bean

新建一個Shiro配置類,配置Shiro最爲核心的安全管理器SecurityManager。web

@Bean
    public SecurityManager securityManager(UserAuthorizingRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setRememberMeManager(null);
        return securityManager;
    }

再配置Shiro的過濾器工廠類,將上一步配置的安全管理器注入,並配置相應的過濾規則。spring

@Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 登陸頁面,無權限時跳轉的路徑
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 配置攔截規則
        Map<String, String> filterMap = new LinkedHashMap<>();
        // 首頁配置放行
        filterMap.put("/", "anon");
        // 登陸頁面和登陸請求路徑須要放行
        filterMap.put("/login", "anon");
        filterMap.put("/do_login", "anon");
        // 其餘未配置的全部路徑都須要經過驗證,不然跳轉到登陸頁
        filterMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        return shiroFilterFactoryBean;
    }

上面使用LinkedHashMap是爲了保持順序,Filter的配置順序不能隨便打亂,過濾器是按照咱們配置的順序來執行的。範圍大的過濾器要放在後面,/**這條若是放在前面,那麼一來就匹配上了,就不會繼續再日後走了。這裏的對上面用到的兩個過濾器作一下簡單說明,篇幅控制其餘過濾器請參閱相關文檔:數據庫

* authc:配置的url都必須認證經過才能夠訪問,它是Shiro內置的一個過濾器
* 對應的實現類 @see org.apache.shiro.web.filter.authc.FormAuthenticationFilter

* anon:也是Shiro內置的,它對應的過濾器裏面是空的,什麼都沒作,能夠理解爲不攔截
* 對應的實現類 @see org.apache.shiro.web.filter.authc.AnonymousFilter

實現兩個方法

在上一步的安全管理器配置中,咱們經過形參注入了一個UserAuthorizingRealm對象,這個就是認證和受權相關的流程,須要咱們本身實現。繼承AuthorizingRealm以後,咱們須要實現兩個抽象方法,一個是認證,一個是受權,這兩個方法長得很像,別弄混淆了。apache

doGetAuthenticationInfo():認證。至關於登陸,只有經過登陸了,才能進行後面受權的操做。一些只須要登陸權限的操做,在登陸成功後就能夠訪問了,好比上一步中配置的authc過濾器就是隻須要登陸權限的。數組

doGetAuthorizationInfo():受權。認證事後,僅僅擁有登陸權限,更多細粒度的權限控制,好比菜單權限,按鈕權限,甚至方法調用權限等,均可以經過受權輕鬆實現。在這個方法裏,咱們能夠拿到當前登陸的用戶,再根據實際業務賦予用戶部分或所有權限,固然這裏也能夠賦予用戶某些角色,後面也能夠根據角色鑑權。下方的演示代碼僅添加了權限,賦予角色能夠調用addRoles()或者setRoles()方法,傳入角色集合。

public class UserAuthorizingRealm extends AuthorizingRealm {

    @Autowired
    private LoginService loginService;

    /**
     * 受權驗證,獲取受權信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user = (User) principalCollection.getPrimaryPrincipal();
        List<String> perms;
        // 系統管理員擁有最高權限
        if (User.SUPER_ADMIN == user.getId()) {
            perms = loginService.getAllPerms();
        } else {
            perms = loginService.getUserPerms(user.getId());
        }

        // 權限Set集合
        Set<String> permsSet = new HashSet<>();
        for (String perm : perms) {
            permsSet.addAll(Arrays.asList(perm.trim().split(",")));
        }

        // 返回權限
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }

    /**
     * 登陸驗證,獲取身份信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        // 獲取用戶
        User user = loginService.getUserByUsername(token.getUsername());
        if (user == null) {
            throw new UnknownAccountException("帳號或密碼不正確");
        }
        // 判斷用戶是否被鎖定
        if (user.getStatus() == null || user.getStatus() == 1) {
            throw new LockedAccountException("帳號已被鎖定,請聯繫管理員");
        }
        // 驗證密碼
        if (!user.getPassword().equals(new String(token.getPassword()))) {
            throw new UnknownAccountException("帳號或密碼不正確");
        }
        user.setSessionId(SecurityUtils.getSubject().getSession().getId().toString());
        // 設置最後登陸時間
        user.setLastLoginTime(new Date());
        // 此處能夠持久化用戶的登陸信息,這裏僅作演示沒有鏈接數據庫
        return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    }
}

這樣配置完成之後,就能夠基於URL作粗粒度的權限控制了,咱們能夠經過不一樣的過濾器爲URL配置不一樣的權限。

Shiro提供了不少內置的過濾器,咱們最經常使用的就是第一個和第二個。若是對其效果不滿意,咱們還能夠自定義過濾器實現權限控制。
文檔地址:http://shiro.apache.org/web.html#default-filters


細粒度權限控制

若是須要更細緻的權限控制,請繼續往下添加配置,能夠作到方法級別的權限控制。其實在SpringMVC中URL也能作到方法級別控制,可是使用URL來控制方法級別的權限配置起來簡直反人類,一般URL權限控制一般都是泛解析,作通用的權限配置,好比後臺管理的/admin/**這種須要登陸權限的。在實際開發中註解式的權限控制用的最多。

AdvisorAutoProxyCreator

註解式的權限控制須要配置兩個Bean,第一個是AdvisorAutoProxyCreator,代理生成器,須要藉助SpringAOP來掃描@RequiresRoles和@RequiresPermissions等註解,生成代理類實現功能加強,從而實現權限控制。須要配合AuthorizationAttributeSourceAdvisor一塊兒使用,不然權限註解無效。

@Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator autoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        autoProxyCreator.setProxyTargetClass(true);
        return autoProxyCreator;
    }

AuthorizationAttributeSourceAdvisor

上面配置的DefaultAdvisorAutoProxyCreator至關於一個切面,下面這個類就至關於切點了,兩個一塊兒才能實現註解權限控制。

@Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

配置完上面兩個Bean以後咱們就可使用註解來控制權限了,Shiro中的權限註解有不少,咱們最經常使用的其實就兩個,@RequiresRoles和@RequiresPermissions,前者是角色驗證,後者是權限驗證。他們均可以傳入兩個參數,value是必須的,能夠傳入一個字符數組,表示一個或多個角色(權限),另外一個參數logical有兩個值可選,AND和OR,默認爲AND,表示這組角色(權限)是必須都有仍是僅須要一個就能訪問。

舉個栗子:

@RequestMapping("getLoginUserInfo")
    @RequiresPermissions(value = {"user:list", "user:info"}, logical = Logical.OR)
    public JsonResult getLoginUserInfo() {
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        return JsonResult.ok(user);
    }

以上代碼表示getLoginUserInfo()方法須要當前登陸用戶擁有user:list或者user:list權限才能訪問。

最後放上項目代碼,其實代碼是很早以前的,今天才作的筆記而已。

Gitee:https://gitee.com/guitu18/ShiroDemo

GitHub:https://github.com/guitu18/ShiroDemo


本篇結束,Shiro的使用仍是很是簡單的。下一篇,準備記錄一下基於Springboot和Shiro使用Redis實現集羣環境的Session共享,以實現單點登陸。

相關文章
相關標籤/搜索