Shiro集成到Spring

爲什麼要集成Spring?

我們的項目基本都是通過Spring來管理bean的,如果要想使用Shiro,那就要把shiro集成到Spring。集成Spring的核心就是把框架的核心類(SecurityManager,Subject,Realm)交給Spring管理

集成

第一步- -在自己的項目中導入相關的jar包

<!-- shiro的支持包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-all</artifactId>
  <version>1.4.0</version>
  <type>pom</type>
</dependency>
<!-- shiro與Spring的集成包 -->
<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
</dependency>

第二步- -配置文件

在web.xml中配置shiroFilter

Spring與shiro集成:需要定義一個shiro過濾器(這是一個代理過濾器,它會到spring的配置中找一個名稱相同的真實過濾器)

<filter>
  <filter-name>shiroFilter</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  <init-param>
    <param-name>targetFilterLifecycle</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>shiroFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

配置applicationContext-shiro.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <!--spring管理shiro的核心對象-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="itsourceRealm"/>
    </bean>


    <!--配置自定義realm-->
    <bean id="itsourceRealm" class="cn.itsource.aisell.realm.ItsourceRealm">
        <!--密碼匹配器-->
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="MD5"/>
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>

    <!--
        shiro的核心bean
        注意: 該bean的id必須要和web.xml中代理過濾器的名字一致,否則要報錯

    -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <!--如果你沒有認證通過,直接跳到loginUrl對應的頁面-->
        <property name="loginUrl" value="/login/login.jsp"/>
        <!--登錄成功之後,跳轉的指定的界面-->
        <property name="successUrl" value="/login/success.jsp"/>

       <!-- 如果沒有對應的權限則跳到unauthorizedUrl對應的界面-->
        <property name="unauthorizedUrl" value="/login/unauthorized.jsp"/>
       <!-- 配置過濾器鏈-->
        <!--
        這種方式真實開發不用,因爲數據寫死了的,靈活性較差
         <property name="filterChainDefinitions">
             <value>
                     anon:匿名過濾器,直接放行
                     authc:必須認證通過之後,才能放行
                     logout:登出
                 /s/login.jsp = anon
                 /login=anon
                 /logout=logout
                 /employee/index=perms["employee:*"]
                 /dept/index=perms["dept:index"]  訪問/dept/index必須要擁有dept:index權限
                /** = authc
            </value>
        </property>
        -->
       <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
        <!--自定義過濾器權限-->
        <property name="filters">
            <map>
                <entry key="itsourceFilter">
                    <bean class="cn.itsource.aisell.realm.ItsourcePermissionsAuthorizationFilter"/>
                </entry>
            </map>
        </property>
    </bean>
    <bean id="chainDefinitionMap" class="cn.itsource.aisell.realm.ItsourceFilterChainDefinitionMap"/>
    <bean id="filterChainDefinitionMap" factory-bean="chainDefinitionMap" factory-method="createMap"/>

</beans>

在applicationContext.xml中引入shiro文件

<!-- 引入shiro.xml文件-->
<import resource="classpath:applicationContext-shiro.xml"/>

在配置文件中我們自定義了Realm過濾器權限 還有配置過濾器鏈
因爲現有的Realm都不能滿足我們的需求 而這種情況下,就需要我們自己定義一個Realm來實現相應的功能
自定義Realm一般直接繼承AuthorizingRealm接口即可(裏面包含身份認證與授權兩個方法)
什麼是Realm?

Realm:域,Shiro 從 Realm 獲取安全數據(如用戶、角色、權限),就是說 SecurityManager 要驗證用戶身份,那麼它需要從 Realm 獲取相應的用戶進行比較以確定用戶身份是否合法;也需要從 Realm 得到用戶相應的角色/權限進行驗證用戶是否能進行操作;可以把 Realm 看成 DataSource , 即 安 全 數 據 源 。(摘自《跟我學Shiro》

自定義Realm
ItsourceRealm

import cn.itsource.aisell.domain.Employee;
import cn.itsource.aisell.service.IEmployeeService;
import cn.itsource.aisell.service.IPermissionService;
import cn.itsource.aisell.util.Md5Util;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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.springframework.beans.factory.annotation.Autowired;

import java.util.Set;

public class ItsourceRealm extends AuthorizingRealm {

    @Autowired
    private IEmployeeService employeeService;

    @Autowired
    private IPermissionService permissionService;

    /**
     * 授權方法
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        Set<String> permissions = permissionService.findPermissionsByUserLogin();
        info.setStringPermissions(permissions);
        return info;
        }

    /**
     * 認證方法
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //獲取用戶名
        String principal = (String) token.getPrincipal();
        //根據用戶名查找用戶
        Employee employee = employeeService.findByUsername(principal);

        if (employee == null) {
            return null;
        }

        ByteSource salt = ByteSource.Util.bytes(Md5Util.SALT);

        //當前的realm名  getName()
        return new SimpleAuthenticationInfo(employee,employee.getPassword(),salt,getName());
    }

}

自定義過濾器鏈

ItsourceFilterChainDefinitionMap.java

public class ItsourceFilterChainDefinitionMap {

    @Autowired
    private IPermissionService permissionService;

    public Map<String,String> createMap(){
        Map<String, String> map = new LinkedHashMap<>();
        map.put("/login/login.jsp", "anon");
        map.put("/login", "anon");
        map.put("/login/**", "anon");
        map.put("/forget/forget", "anon");
        map.put("/static/**", "anon");
        map.put("/*.jsp", "anon");
        map.put("/logout", "logout");

        List<Permission> all = permissionService.findAll();
        for (Permission permission : all) {
            map.put(permission.getUrl(),"itsourceFilter["+permission.getSn()+"]");
        }

        map.put("/**", "authc");
        /*
        anon:匿名過濾器,直接放行
        authc:必須認證通過之後,才能放行
        logout:登出
        */
        return map;
    }
}

Authentication 身份認證

LoginController.java


@Controller
public class LoginController {
 
    @RequestMapping("/login")
    @ResponseBody
    public String login(String username,String password){
        Subject currentUser = SecurityUtils.getSubject();  //拿到當前用戶
        if(!currentUser.isAuthenticated()){ //如果這個用戶沒有登錄,則進行登錄認證
            try {
                UsernamePasswordToken token = new UsernamePasswordToken(username,password);
                //調用login方法時底層會去調用realm中的認證方法
                currentUser.login(token);
                return new AjaxResult();
            }catch (UnknownAccountException e) {
                e.printStackTrace();
                return new AjaxResult(false, "賬號不存在");
            }catch (IncorrectCredentialsException e) {
                e.printStackTrace();
                return new AjaxResult(false, "密碼錯誤");
            } catch (AuthenticationException e) {
                e.printStackTrace();
                //當出現這個錯誤時候 就說明是代碼哪裏錯了
                return new AjaxResult(false, "網絡繁忙請稍後再試");
            }
        }
    }
 
    @RequestMapping("/logout")
    public String logout(){
        Subject currentUser = SecurityUtils.getSubject();
        currentUser.logout();
        return new AjaxResult(true, "註銷成功");
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章