shiro在項目中的使用(一)

【前言】
Apache Shiro是Java的一個安全框架。主要用于權限控制,簡單易用。之前單看shiro始終理會不了它的思想,后來在網上發現某智的一個bos項目中運用到了shiro,果斷學習,看完后略有心得,以記之。

【項目簡介】
該bos項目主要是一個后臺管理項目,采用傳統ssh技術架構,使用spring與shiro進行整合做權限攔截,以下是項目中權限部分的筆記。

首先,我們從外部來看Shiro吧,即從應用程序角度的來觀察如何使用Shiro完成工作

shiro工作流程.png
Subject:主體,代表了當前“用戶”

SecurityManager:安全管理器;即所有與安全有關的操作都會與SecurityManager交互

Realm:域,Shiro從從Realm獲取安全數據(如用戶、角色、權限)

用戶主體訪問系統,由安全管理器進行權限驗證,安全管理器從Realm域中獲取到該用戶的角色權限,并作出相應的權限反饋,Realm域就是一個Dao,主要獲取用戶的角色權限數據并交給安全管理器。所以整個流程中,安全管理器是核心角色。

【1】web.xml中配置shiro的過濾器
shiro的過濾器就類似于struts2的核心過濾器一般

<!-- shiro過濾器 -->
    <filter>
        <filter-name>shiroFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>shiroFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

注意:這里filterName的值可以隨便起,無要求。但是在spring注入時要保持一致

【2】將shiro過濾器注入spring

<!-- 配置工廠bean,用于創建shiro框架用到過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- 注入安全管理 -->
        <property name="securityManager" ref="securityManager"></property>
        <!-- 注入當前系統的登錄頁面 -->
        <property name="loginUrl" value="/login.jsp"></property>
        <!-- 注入成功頁面 -->
        <property name="successUrl" value="/index.jsp"></property>
        <!-- 注入權限不足頁面 -->
        <property name="unauthorizedUrl" value="/unauthorizedUrl.jsp"/>
        <!-- 注入url攔截規則 -->
        <property name="filterChainDefinitions">
            <value>
                /css/** = anon
                /images/** = anon
                /js/** = anon
                /login.jsp* = anon
                /validatecode.jsp* = anon
                /userAction_login.action = anon
                /page_base_staff.action = perms["staff"]<!-- roles角色集,perms權限集 -->
                /* = authc
            </value>
            <!-- 
                /page_base_staff.action = roles["staff"]//要訪問此action必須有staff這個角色
                /page_base_staff.action = perms["staff"]//要訪問此action必須有staff這個權限
             -->
        </property>
    </bean>

注意:以上屬性均以set注入,查看ShiroFilterFactoryBean源碼便知,業務需要配哪個屬性就配哪個,非必要配置,這里頁面以/開頭均在webroot目錄下。

這里bean 的id屬性要與web.xml中shiro的過濾器名稱一致

url攔截規則:一般圖片、JS、CSS樣式不攔截,直接設置anon角色權限即可(/css/** = anon)
具體url攔截:
    /page_base_staff.action = perms["staff"]//訪問此action需要staff權限
    /page_base_staff.action =roles["staff"]//訪問此action需要staff角色(角色是權限的集合)

要攔截的路徑:/* = authc

【3】編寫自定義Realm域并注入到spring中
自定義Realm需要繼承AuthorizingRealm,實現其認證 授權方法

/**
 * ClassName: BOSRealm
 * @author lvfang
 * @Desc: 自定義realm
 * @date 2017-8-23
 */
public class BOSRealm extends AuthorizingRealm {
    
    @Resource
    private IUserDao userDao;
    
    /**
     * 認證方法(是否有這個用戶)
     */
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        System.out.println("進入認證方法... ...");
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;//token令牌強轉
        String username = upToken.getUsername();//從令牌中得到用戶名
        
        //查詢用戶
        User user = userDao.findUserByUsername(username);
        if(user == null){
            //用戶名不存在
            return null;
        }else{
            //用戶名存在
            String password = user.getPassword();
            
            // 創建簡單認證信息對象
            /***
             * 參數一:簽名,程序可以在任意位置獲取當前放入的對象
             * 參數二:從數據庫中查詢出的密碼
             * 參數三:當前realm的名稱
             */
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user,password,this.getClass().getSimpleName());
            //返回給安全管理器,由安全管理器負責比對數據庫中查詢出的密碼和頁面提交的密碼
            return info;
        }   
    }

    /**
     * 授權方法(這個用戶有什么權限)
     */
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("進入授權方法... ...");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermission("staff");//為當前用戶授予staff權限
        info.addRole("staff");//為當前用戶授予staff角色(角色是權限的集合)    
        //TODO 根據當前登錄用戶查詢數據庫,獲取其對應的權限數據
        
        return info;
    }
}

注入spring

<!-- 注冊自定義realm -->
    <bean id="bosRealm" class="com.itheima.bos.shiro.BOSRealm"></bean>

【4】注入安全管理器,并將realm注入給安全管理器

<!-- 注入 securityManager管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 將自定義realm注入給securityManager管理 -->
        <property name="realm" ref="bosRealm"></property>
    </bean>

【5】在login方法中做處理

/**
     * 登陸方法(shiro版)
     * @return
     */
    public String login(){  
        //判斷驗證碼
        String code = (String) this.setSession("key");
        //判斷驗證碼是否正確
        if(StringUtils.isNotBlank(checkcode)&& checkcode.equals(code)){
            
            //獲取當前用戶對象
            Subject subject = SecurityUtils.getSubject();//目前為"未認證狀態"
            String password = model.getPassword();
            password = MD5Utils.md5(password);
            //構造一個用戶名密碼令牌
            AuthenticationToken token = new UsernamePasswordToken(model.getUsername(),password);
            
            try {
                subject.login(token);
            } catch (UnknownAccountException e) {
                e.printStackTrace();
                //登陸失敗  設置提示信息,跳轉登陸頁面
                this.addActionError("用戶名不存在!");
                return "login";
            } catch (Exception e) {
                e.printStackTrace();
                //登陸失敗  設置提示信息,跳轉登陸頁面
                this.addActionError("用戶名或密碼錯誤!");
                return "login";
            }
            
            //獲取認證信息對象中存儲的User對象
            User user = (User) subject.getPrincipal();
            this.getSession().setAttribute("loginUser", user);//用戶存入session
            return "home";
        }else{
            //登陸失敗,驗證碼失敗 跳轉至登陸頁面
            this.addActionError("驗證碼錯誤!");
            return "login";
        }   
    }

這里根據username和password構造一個用戶名令牌,當subject主體去調用login()方法登陸時,會執行Realm域中的認證和授權方法。

【附加】

1:
shiro過濾器屬性含義

securityManager:這個屬性是必須的。

loginUrl :沒有登錄的用戶請求需要登錄的頁面時自動跳轉到登錄頁面,不是必須的屬性,不輸入地址的話會自動尋找項目web項目的根目錄下的”/login.jsp”頁面。

successUrl :登錄成功默認跳轉頁面,不配置則跳轉至”/”。如果登陸前點擊的一個需要登錄的頁面,則在登錄自動跳轉到那個需要登錄的頁面。不跳轉到此。

unauthorizedUrl :沒有權限默認跳轉的頁面


2:
其權限過濾器及配置釋義

anon:例子/admins/**=anon 沒有參數,表示可以匿名使用。

authc:例如/admins/user/**=authc表示需要認證(登錄)才能使用,沒有參數

roles(角色):例子/admins/user/**=roles[admin],參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,當有多個參數時,例如admins/user/**=roles["admin,guest"],每個參數通過才算通過,相當于hasAllRoles()方法。

perms(權限):例子/admins/user/**=perms[user:add:*],參數可以寫多個,多個時必須加上引號,并且參數之間用逗號分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],當有多個參數時必須每個參數都通過才通過,想當于isPermitedAll()方法。

rest:例子/admins/user/**=rest[user],根據請求的方法,相當于/admins/user/**=perms[user:method] ,其中method為post,get,delete等。

port:例子/admins/user/**=port[8081],當請求的url的端口不是8081是跳轉到schemal://serverName:8081?queryString,其中schmal是協議http或https等,serverName是你訪問的host,8081是url配置里port的端口,queryString

是你訪問的url里的?后面的參數。

authcBasic:例如/admins/user/**=authcBasic沒有參數表示httpBasic認證

ssl:例子/admins/user/**=ssl沒有參數,表示安全的url請求,協議為https

user:例如/admins/user/**=user沒有參數表示必須存在用戶,當登入操作時不做檢查

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,981評論 19 139
  • 前言 Spring boot 是什么,網上的很多介紹,這里博客就不多介紹了。如果不明白Spring boot是什么...
    xuezhijian閱讀 17,944評論 13 39
  • 1.簡介 Apache Shiro是Java的一個安全框架。功能強大,使用簡單的Java安全框架,它為開發人員提供...
    H_Man閱讀 3,184評論 4 47
  • Spring Boot 參考指南 介紹 轉載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,970評論 6 342
  • 雨突然 下得好猛 他沒帶傘 躲在路邊屋檐 發呆 那傘下的姑娘們 行色勿勿 他問 白娘子呀 你眼淚漣漣找啥呢 許仙又...
    雪莉詩話閱讀 161評論 1 12