我是這樣用shiro的

小述

我在shiro學習上花費了一些時間,shiro的資料網上一大推,以前本身學習的知識點一直記錄在有道雲筆記上,有道雲有本身的好處 那就是沒有網絡的時候依然能夠記錄一些東西,可是弊端就是不能與你們一塊兒分享和討論,最後仍是選擇在segmentFault,一上來發現本身聲望值是負數,有點小悲傷啊,之後學習到東西后本身會在這裏寫寫記記,一是概括梳理知識且本身記性很差,方便之後自我回憶,二是但願能和你們討論,我有不對的地方但願能有大神指點。廢話很少說,寫一下我最近學習的shiro的用法。java


正文

圖片描述

大致的登陸認證流程如上圖,當進入登陸頁面後,會先進入經過shiroFilter安全認證過濾器,而後讀取數據庫信息來進行登陸和受權的認證,這一部分是交給realm來進行的,當認證成功後會跳轉到successURL對應的地址中去若是失敗會跳轉到loginUrl中。圖上的的${adminPath}只是從配置文件properties中讀出配置參數罷了,能夠把它當作/a。web


web.xml配置

<!-- Apache Shiro -->
    <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>

web.xml基本上是固定模板,/*攔截了全部的請求,可是記住下shiroFilter這個名字。spring


spring-context-shiro.xml 配置

<description>Shiro Configuration</description>

    <!-- 加載配置屬性文件 -->
    <context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />
    
    <!-- (會被引入)Shiro權限過濾過濾器定義 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /static/** = anon
                /userfiles/** = anon
                ${adminPath}/login = authc
                ${adminPath}/logout = logout
                ${adminPath}/sys/** = roles[sys]
                ${adminPath}/cms/** = perms[cms:view]
                ${adminPath}/** = user
            </value>
        </constructor-arg>
    </bean>
    
    <!-- (擴展)安全認證過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" /> 
        <property name="loginUrl" value="${adminPath}/login" />
        <property name="successUrl" value="${adminPath}/success" />
        <property name="unauthorizedUrl" value="/unauthorized.jsp" />
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
        <property name="filterChainDefinitions">
            <ref bean="shiroFilterChainDefinitions"/>
        </property>
    </bean>
    
    <!-- (擴展)定義Shiro安全管理配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 經過realm來讀取身份信息和受權信息 -->
        <property name="realm" ref="systemAuthorizingRealm" />
    </bean>
    
    <!-- (固定)保證明現了Shiro內部lifecycle函數的bean執行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!--(固定) AOP式方法級權限檢查  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

XML配置文件中不少也是固定不變的,官網文檔也有介紹,anon表示請求對應的路徑不認證,authc表示請求對應的路徑須要登陸認證,roles[sys]表示請求對應的路徑須要角色爲sys的才容許,perms[cms:view]表示請求對應的路徑須要有cms:view權限的才容許。這裏的安全認證過濾器的名字要與web.xml中的shiroFilter同樣。數據庫

<property name="loginUrl" value="${adminPath}/login" />

表示登陸失敗後會跳轉到對應請求apache

<property name="successUrl" value="${adminPath}/success" />

表示登陸成功後會跳轉的請求安全

<property name="unauthorizedUrl" value="/unauthorized.jsp" />

表示訪問了無權訪問的連接後跳轉的請求。網絡

<entry key="authc" value-ref="formAuthenticationFilter"/>

表示 authc這個會經過FormAuthenticationFilter這個類來驗證app

<property name="realm" ref="systemAuthorizingRealm" />

這是咱們自定義的realm來認證登陸和受權信息的jsp

圖片描述

實驗的目錄結構,如上圖ide

圖片描述


重寫FormAuthenticationFilter類

@Service
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter{
    
    public static final String DEFAULT_CAPTCHA_PARAM = "validateCode";
    
    private String captchaParam = DEFAULT_CAPTCHA_PARAM;

    @Override
    protected AuthenticationToken createToken(ServletRequest request,ServletResponse response) {
        System.out.println("-------------進入建立token方法-------------");
        // TODO Auto-generated method stub
        String username = getUsername(request);
        String password = getPassword(request);
        if(password == null){
            password = "";
        }
        String captcha = getCapcha(request);
        System.out.println("-------------出建立token方法-------------");
        return new UsernamePasswordToken(username,password,captcha);
    }
    
    
    public String getCapcha(ServletRequest request){
        return WebUtils.getCleanParam(request, captchaParam);
    }
    
    public String getSuccessUrl(){
        return super.getSuccessUrl();
    }
    
    @Override
    protected void issueSuccessRedirect(ServletRequest request,ServletResponse response) throws Exception {
        // TODO Auto-generated method stub、
        System.out.println("-------------issueSuccessRedirect-------------");
        WebUtils.issueRedirect(request, response, getSuccessUrl());
    }


    @Override
    protected boolean onLoginFailure(AuthenticationToken token,AuthenticationException e, ServletRequest request,
            ServletResponse response) {
        // TODO Auto-generated method stub
        System.out.println("-------------onLoginFailure-------------");
        String className = e.getClass().getName();
        String message = "";
        System.out.println("=========e.getCLass().getName()===========:"+className);
        if(IncorrectCredentialsException.class.getName().equals(className)
                || UnknownAccountException.class.getName().equals(className)){
            message = "用戶名或密碼錯誤";
        }else{
            message = "系統出現點問題,請稍後再試!";
            e.printStackTrace(); // 輸出到控制檯
        }
        request.setAttribute("message",message);
        return true;
    }

}

當咱們輸入完用戶密碼後須要進入一個 shiroFilter 安全認證過濾器,這裏爲何須要重寫了呢,其實本質是shiro已經給咱們提供了相應的 FormAuthenticationFilter 類,能夠在其中根據用戶輸入的信息建立 token 來供之後流程的認證,可是若是咱們還想加入一些其餘的東西來一塊兒建立這個 token,好比說咱們經過用戶名、密碼、驗證碼、是否記住我、是不是手機端登陸等等信息來一塊兒組成一個token的時候,這個時候咱們就能夠重寫 FormAuthenticationFilter 中的一些方法來實現了。

WebUtils.getCleanParam(request, captchaParam) 是 shiro 給咱們提供的封裝,其實就是 request.getParameter(paramName) 的一個封裝罷了,咱們能夠經過它來獲取前臺輸入的參數。

重寫UsernamePasswordToken類

public class UsernamePasswordToken extends org.apache.shiro.authc.UsernamePasswordToken{
    
    private static final long serialVersionUID = 1L;
    
    private String captcha;
    
    public UsernamePasswordToken(String username,String password){
        super(username,password);
    }

    public UsernamePasswordToken() {
        super();
    }
    
    public UsernamePasswordToken(String username,String password,String captcha){
        super(username,password);
        this.captcha = captcha;
    }

    public String getCaptcha() {
        return captcha;
    }

    public void setCaptcha(String captcha) {
        this.captcha = captcha;
    }
    
}

經過重寫UsernamePasswordToken,目的是來根據開發者想建立token的參數來構造出一個新的UsernamePasswordToken以供使用。我這裏爲了簡化只寫了用戶名密碼參數。

此時此刻咱們就有了token,下面開始咱們的認證啦~

經過自定義的 Realm 來認證受權

@Service
public class SystemAuthorizingRealm extends AuthorizingRealm{
    
    @Autowired
    private SystemService systemService;
    /**
     * 登陸認證
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        // TODO Auto-generated method stub
        System.out.println("-------------進入登陸認證方法-------------");
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String username = token.getUsername();
        User user = systemService.getUserByName(username);
        System.out.println("-------1----getName()--------:"+getName());
        if(user != null){
            System.out.println("-------2----getName()--------:"+getName());
            System.out.println("-------------登陸認證結束--返回SimpleAuthenticationInfo-------------");
            return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
        }else{
            System.out.println("------------登陸認證結束--返回null------------");
            return null;
        }
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        // TODO Auto-generated method stub
        System.out.println("-------------進入受權認證方法-------------");
//        Principal principal = (Principal) getAvailablePrincipal(principals);
        String name = (String) principals.getPrimaryPrincipal();
//        User user = systemService.getUserByName(principal.getName());
        User user = systemService.getUserByName(name);
        if(user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            List<Role> Roles = systemService.getRoleByUserId(user.getId());
            for(Role r:Roles){
                System.out.println("role---> "+r.getRole());
                info.addRole(r.getRole());
                List<Perm> Perms = systemService.getPermByRoleId(r.getId());
                if(Perms != null && Perms.size() >0 ){
                    for(Perm p:Perms){
                        System.out.println("perm---> "+p.getPermission());
                        info.addStringPermission(p.getPermission());
                    }
                }
            }
            System.out.println("-----------受權認證結束--返回info------------");
            return info;
        }else{
            System.out.println("-----------受權認證結束--返回null------------");
            return null;
        }
    }
}

在 realm 中咱們須要繼承 AuthorizingRealm,並重寫兩個方法:

  1. doGetAuthenticationInfo 方法:用戶身份認證方法,根據參數返回
    SimpleAuthenticationInfo,若是登陸成功則跳轉到xml配置文件中的 successUrl 地址,若是登陸失敗則跳轉到 loginUrl 地址;

  2. doGetAuthorizationInfo 方法:身份權限認證,利用了 SimpleAuthorizationInfo,把角色和權限都給予 SimpleAuthorizationInfo 來進行受權;在是 shiro 中若是訪問了無權訪問的地址,則會跳轉到 xml 配置文件中
    unauthorizedUrl 對應的地址。

到這裏基本上shiro就完畢了,當咱們登陸時候的順序:

圖片描述

上圖爲登陸成功的流程

clipboard.png

上圖爲登陸失敗的流程

這時候 shiro 已經記住了用戶的信息,當再請求路徑的時候就不會繼續驗證了,在此輸入請求攔鏈接的時候就會直接去找對應的 action 而不會再進入安全過濾器,全部咱們想在此登陸須要退出當前用戶才行。以下代碼能夠退出當前用戶,這樣咱們再請求登陸的時候就會繼續安全認證了。

SecurityUtils.getSubject().logout();

當咱們登陸成功後,咱們用是sys角色可是沒有 perms[cms:view] 權限 的用戶訪問 a/sys 是能夠請求成功,可是訪問 a/cms 是會跳轉到 shiro 的 xml 配置文件的unauthorizedUrl 對應的地址,表示權限不足不可訪問。

clipboard.png

上圖爲受權成功,用戶有該請求權限,跳轉到對應 action 中

clipboard.png

上圖爲受權失敗,跳轉到 unauthorized.jsp 頁面,提示無權訪問

之後會有新的內容會加已補充,好比shiro的標籤和shiro和ehcache的整合等等。

相關文章
相關標籤/搜索