1. 程式人生 > >Shiro實現(一): SSM整合筆記實現登入,授權功能

Shiro實現(一): SSM整合筆記實現登入,授權功能

開篇

  1. 本專案已經上傳github,建議對照程式碼理解
  2. 本篇主要講Shiro框架與SSM框架結合,實現登入和授權功能
  3. 利用spring 的aop切面思想,很簡單得融合Shiro許可權框架
  4. 程式碼

需要明白兩個點:

  1. 通過Subject.login() 登入成功後,使用者資訊就會儲存在安全管理器上,也就是 SecurityManager。就可以在程式任何地方獲取到該使用者物件。
  2. 在重寫攔截器兩個方法是重點,在登入的時候就需要把授權資訊也存到安全管理器上,所以登入成功後,所有判斷許可權都不需要在業務邏輯上做判斷,shiro框架已經幫你攔截並判斷好。

1. jar包

shiro有很多種型別的包,用途有web的,非web的等,*-all 代表所有都在裡面
shiro-all.jar

2.配置

其實shiro許可權控制就是通過攔截器來進行判斷使用者許可權的,因此shiro攔截器的配置跟springMVC的攔截器配置是類似的。

第一步

盡然是通過aop來使用shiro,那就需要在web.xml裡新增一個shiro的攔截器。

ps:這個shiro攔截器是如何載入的呢? 因為這個專案shiro與spring整合了,所有執行專案的時候,spring監聽器會去尋找並載入shiro攔截器

 <!-- Spring監聽器 -->
org.springframework.web.context.ContextLoaderListener > ```

shiro攔截器配置如下:

<!-- 配置由Spring提供的過濾器,用於整合shiro框架 -->
  <!-- 在專案啟動的過程中,當前過濾器會從Spring工廠中提取同名物件 -->
  <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>

第二步

類似springmvc一樣,需要寫一個配置檔案來配置攔截器。
在shiro配置檔案中,有兩種型別。
- ini配置檔案 (很多部落格教程上都是使用這種配置方式。)
- xml配置檔案 (這次專案使用這種配置方式)

首先建立shiro-context.xml

ps: 很簡單,就相當於spring的配置檔案一樣,因為shiro是跟spring很好結合的。
下面會慢慢解釋這個配置檔案

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
		<property name="securityManager" ref="securityManager" /> <!--載入管理器-->
		<property name="loginUrl" value="/user/login" />    <!--沒有登入的時候,跳轉到這個頁面-->
		<property name="unauthorizedUrl" value="/user/nopermission" /> <!--當沒有許可權的時候,跳轉到這個url-->
		 
		<property name="filterChainDefinitions">
			<value>
				/user/login = anon <!--可以不需要登入-->
				/user/readName = authc, perms[/readName]  <!-- perms 表示需要該許可權才能訪問的頁面 -->
				/user/readData = authc, perms[/readData]
				/user/* = authc <!-- authc 表示需要認證才能訪問的頁面 -->
			</value>
		</property>
	</bean>

	<!-- 自定義Realm -->
	<bean id="myShiroRealm" class="com.Shiro.MyShiroReaml">
		<!-- businessManager 用來實現使用者名稱密碼的查詢 -->
		<property name="shiroService" ref="accountService" />
	</bean>

	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<!-- 注入realm -->
		<property name="realm" ref="myShiroRealm"/>
	</bean>

	<!--宣告一個Service 注入到自定義Realm-->
	<bean id="accountService" class="com.Service.Impl.ShiroServiceImpl"/>
	<!-- <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 
		<property name="cacheManager" ref="cacheManager" /> </bean> -->
</beans>
  1. 攔截器需要載入一個安全管理器,SecurityManager 是整個shiro框架的核心
	<property name="securityManager" ref="securityManager" /> <!--載入管理器-->
  1. 攔截url,這裡就是配置攔截url。anon.authc.這些名詞其實就是shiro已經寫好的攔截器,只需要呼叫可以了。在如果許可權不夠,則會跳轉到指定的url
  <property name="filterChainDefinitions">
			<value>
				/user/login = anon <!--可以不需要登入-->
				/user/readName = authc, perms[/readName]  <!-- perms 表示需要該許可權才能訪問的頁面 -->
				/user/readData = authc, perms[/readData]
				/user/* = authc <!-- authc 表示需要認證才能訪問的頁面 -->
			</value>
		</property>

過濾器列表:

208783.jpg

第三步 攔截後,對使用者進行驗證

當你攔截器設定好了,可以成功攔截使用者的操作,然後我們需要對使用者進行許可權驗證。所以我們需要繼承shiro的AuthorizingRealm攔截器,重寫兩個方法。
- 重寫doGetAuthenticationInfo方法是:登入驗證,當需要登入的時候,就會呼叫該方法進行驗證。
- 重寫doGetAuthorizationInfo方法:這個是授權驗證,與上面的過濾器相結合。

思路如下:

  • 登入驗證: 根據賬號從資料庫獲取賬號密碼進行比較,如果一致則登入成功,就會儲存到,否則登入失敗
  • 授權驗證:在登入成功後,根據使用者id獲取到該使用者的許可權,並把許可權儲存在安全管理器之中,當用戶訪問的時候,會從管理器中判斷該使用者是否有許可權去訪問該url。
    程式碼如下:
public class MyShiroReaml extends AuthorizingRealm {
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {

        /**
         * 
         * 流程
         * 1.根據使用者user->2.獲取角色id->3.根據角色id獲取許可權permission
         */
        //方法一:獲得user物件
        User user=(User)pc.getPrimaryPrincipal();
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //獲取permission
        if(user!=null) {
            List<Permission> permissionsByUser = shiroService.getPermissionsByUser(user);
            if (permissionsByUser.size()!=0) {
                for (Permission p: permissionsByUser) {

                    info.addStringPermission(p.getUrl());
                }
                return info;
            }
        }

        //方法二: 從subject管理器裡獲取user
//      Subject subject = SecurityUtils.getSubject();
//      User _user = (User) subject.getPrincipal();
//      System.out.println("subject"+_user.getUsername());




        return null;
    }

    // 認證方法
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("進來驗證了");
        //驗證賬號密碼
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        System.out.println("1:"+token.getUsername());
        User user = shiroService.getUserByUserName(token.getUsername());
        System.out.println("2");
        if(user==null){
            return null;
        }
        //最後的比對需要交給安全管理器
        //三個引數進行初步的簡單認證資訊物件的包裝
        AuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getSimpleName());

        return info;
    }


    private ShiroService shiroService;

    public ShiroService getShiroService() {
        return shiroService;
    }

    public void setShiroService(ShiroService shiroService) {
        this.shiroService = shiroService;
    }
}

第四步

shrio的基本配置已經完成了,接下來是基礎程式碼塊了, 也許還有很多疑問,沒關係。下載程式碼下來看看,就明白了

Controller層

1.登入程式碼塊

 @RequestMapping(value = "/login")
    public String Login(String username, String password, HttpSession session, Model model){
        if(username==null){
            model.addAttribute("message", "賬號不為空");
            return "login";
        }
        //主體,當前狀態為沒有認證的狀態“未認證”
        Subject subject = SecurityUtils.getSubject();
        // 登入後存放進shiro token
        UsernamePasswordToken token=new UsernamePasswordToken(username,password);
        User user;
        //登入方法(認證是否通過)
        //使用subject呼叫securityManager,安全管理器呼叫Realm
        try {
            //利用異常操作
            //需要開始呼叫到Realm中
            System.out.println("========================================");
            System.out.println("1、進入認證方法");
            subject.login(token);
            user = (User)subject.getPrincipal();
            session.setAttribute("user",subject);
            model.addAttribute("message", "登入完成");
            System.out.println("登入完成");
        } catch (UnknownAccountException e) {
            model.addAttribute("message", "賬號密碼不正確");
            return "index";
        }
        return "test";
    }

  1. 許可權程式碼塊
shiro已經幫我們驗證了,所以我們只需要寫基本業務邏輯就可以,不需要再寫許可權驗證程式碼了
 @RequestMapping("/readName")
    public String readName(HttpSession session){

        return "name";
    }

    @RequestMapping("/readData")
    public String readData(){

        return "data";
    }


    @RequestMapping("/nopermission")
    public String noPermission(){
        return "error";
    }

Service 層

public class ShiroServiceImpl implements ShiroService {

    @Autowired
    private ShiroDao shiroDao;

    public User getUserByUserName(String username) {
        //根據賬號獲取賬號密碼
        User userByUserName = shiroDao.getUserByUserName(username);
        return userByUserName;
    }

    public List<Permission> getPermissionsByUser(User user) {
        //獲取到使用者角色userRole
        List<Integer> roleId = shiroDao.getUserRoleByUserId(user.getId());
        List<Permission> perArrary = new ArrayList<>();

        if (roleId!=null&&roleId.size()!=0) {
            //根據roleid獲取peimission
            for (Integer i : roleId) {
                perArrary.addAll(shiroDao.getPermissionsByRoleId(i));
            }
        }

        System.out.println(perArrary);
        return perArrary;
    }


}

POJO層

public class Permission {
    private int id;
    private String token;
    /**資源url**/
    private String url;
    /**許可權說明**/
    private String description;
    /**所屬角色編號**/
    private int roleId;
    }

public class Role {
    private int id;
    /**角色**/
    private String role;
    /**說明**/
    private String description;
    }

public class User {
    private int id;
    private String account;
    private String password;
}

第5步 資料庫表的建立

資料表已經附加到程式碼上傳到github
資料庫中新增一條使用者、角色、以及許可權資料,並且在關聯表中新增一條關聯資料

  • Permission 許可權表

Permission.png

  • 角色管理表

角色管理表.png

  • 使用者表

使用者表.png

  • 使用者角色對照表

使用者角色對照表.png

總結

  1. Shiro框架 與spring結合,對原有的程式碼塊改變很少。完美得實現了AOP思想
  2. Shiro的功能很強大,該專案實現了登入已經授權的基本功能,接下來還會有單點登入,快取機制,密碼加密等功能的演示。
  3. 程式碼已放到github上,可以clone下來細看。
  4. 程式碼