SHRIO使用記錄

16年的項目裏用過一次shiro,可是時間久遠又沒有作好筆記,忘得差很少了,此次又走了一遍,記錄一下以便之後翻看,如下是流水。javascript

1.加入依賴前端

<!--shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.3.2</version>
        </dependency>
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache-core</artifactId>
            <version>2.4.3</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.6.1</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!--shiro-->

2.web.xml里加入過濾器java

<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>

3.將shiro相關配置單獨放在applicationContext-shiro.xml中並在spring配置文件中importweb

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

    <context:component-scan base-package="com.wqqd.findfocus" annotation-config="true"/>

    <!-- 1. 配置 SecurityManager-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="cacheManager" ref="cacheManager"/>
        <property name="authenticator" ref="authenticator"></property>
        <property name="realms">
            <list>
                <ref bean="jdbcRealm"/>
            </list>
        </property>
        <!--<property name="rememberMeManager.cookie.maxAge" value="10"></property>-->
    </bean>

    <!--2. 配置 CacheManager(須要加入 ehcache 的 jar 包及配置文件).-->
    <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManagerConfigFile" value="classpath:ehcache.xml"/>
    </bean>

    <bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean><!--多realm時的認證策略-->
        </property>
    </bean>

    <!--3. 配置 Realm(直接配置實現了 org.apache.shiro.realm.Realm 接口的 bean)-->
    <bean id="jdbcRealm" class="com.wqqd.findfocus.shiro.ShiroRealm">
        <property name="credentialsMatcher">
            <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <property name="hashAlgorithmName" value="SHA1"></property>
                <property name="hashIterations" value="10"></property>
            </bean>
        </property>
    </bean>
    <!--<bean id="secondRealm" class="com.atguigu.shiro.realms.SecondRealm">-->
        <!--<property name="credentialsMatcher">-->
            <!--<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">-->
                <!--<property name="hashAlgorithmName" value="SHA1"></property>-->
                <!--<property name="hashIterations" value="1024"></property>-->
            <!--</bean>-->
        <!--</property>-->
    <!--</bean>-->

    <!--
    4. 配置 LifecycleBeanPostProcessor. 能夠自定的來調用配置在 Spring IOC 容器中 shiro bean 的生命週期方法.
    -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

    <!--
    5. 啓用 IOC 容器中使用 shiro 的註解. 但必須在配置了 LifecycleBeanPostProcessor 以後纔可使用.
    -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
          depends-on="lifecycleBeanPostProcessor"/>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!--
   6. 配置 ShiroFilter.
   6.1 id 必須和 web.xml 文件中配置的 DelegatingFilterProxy 的 <filter-name> 一致.
                     若不一致, 則會拋出: NoSuchBeanDefinitionException. 由於 Shiro 會來 IOC 容器中查找和 <filter-name> 名字對應的 filter bean.
   -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/index.do"/>
        <property name="successUrl" value="/welcome.do"/>
        <property name="unauthorizedUrl" value="/unauthorized.do"/>
        <property name="filters">
            <map>
                <entry key="roles">
                    <!--經過攔截器將roles[]裏角色之間的'且'改成'或'-->
                    <bean class="com.wqqd.findfocus.shiro.CustomRolesAuthorizationFilter" />
                </entry>
            </map>
        </property>
        <!--將下面的filterChainDefinitions內容改成動態獲取-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"></property>
        <!--
        	配置哪些頁面須要受保護.
        	以及訪問這些頁面須要的權限.
        	1). anon 能夠被匿名訪問
        	2). authc 必須認證(即登陸)後纔可能訪問的頁面.
        	3). logout 登出.
        	4). roles 角色過濾器
        -->
        <!--<property name="filterChainDefinitions">-->
            <!--<value>-->
                <!--/logout.do = logout-->
                <!--/** = anon-->
            <!--</value>-->
        <!--</property>-->
    </bean>

    <!--經過實例工廠方法的形式注入map-->
    <bean id="filterChainDefinitionMap"
          factory-bean="filterChainDefinitionMapBuilder" factory-method="buildFilterChainDefinitionMap"></bean>
    <bean id="filterChainDefinitionMapBuilder"
          class="com.wqqd.findfocus.shiro.factory.FilterChainDefinitionMapBuilder"></bean>

</beans>

下面是對應的幾個beanajax

ShiroRealmspring

package com.wqqd.findfocus.shiro;

import com.wqqd.findfocus.bean.UserLoginBean;
import com.wqqd.findfocus.common.Const;
import com.wqqd.findfocus.pojo.UserAdmin;
import com.wqqd.findfocus.service.IMenuSysService;
import com.wqqd.findfocus.service.IUserAdminService;
import org.apache.shiro.authc.*;
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.HashSet;
import java.util.Set;

/**
 * @author kexiang.bao
 * @create 2018-09-18 15:59
 */
public class ShiroRealm extends AuthorizingRealm{
    @Autowired
    IUserAdminService iUserAdminService;
    @Autowired
    IMenuSysService iMenuSysService;
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken;
        String userAdminAcct = upToken.getUsername();
        UserAdmin userAdmin = iUserAdminService.getUserAdminByAcct(userAdminAcct);
        if(userAdmin == null){
            throw new UnknownAccountException();
        }

        UserLoginBean userLoginBean = new UserLoginBean();
        userLoginBean.setUserAdmin(userAdmin);
        Object principal = userLoginBean;
        Object credentials = userAdmin.getUserAdminPwd();
        userAdmin.setUserAdminPwd("");
        ByteSource credentialsSalt = ByteSource.Util.bytes(userAdmin.getUserAdminAcct());
        String realmName = super.getName();
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
        return info;
    }

    //受權會被 shiro 回調的方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //1. 從 PrincipalCollection 中來獲取登陸用戶的信息
        Object principal = principals.getPrimaryPrincipal();
        UserLoginBean userLoginBean = (UserLoginBean)principal;
        UserAdmin userAdmin = userLoginBean.getUserAdmin();

        //2. 利用登陸的用戶的信息來用戶當前用戶的角色和權限(可能須要查詢數據庫)
        String roleCode = userAdmin.getRoleCode();

        //超級管理員擁有全部權限
        Set<String> permissionSet = null;
        boolean isSuperAdmin = Const.SUPER_ADMIN_ROLE_CODE.equals(roleCode);
        if(isSuperAdmin){
            permissionSet = iMenuSysService.getAllPermissionCodes();
        }else{
            permissionSet = iMenuSysService.getPermissionCodes(roleCode);
        }
        Set<String> roles = new HashSet<String>();
        roles.add(roleCode);
        //3. 建立 SimpleAuthorizationInfo, 並設置其 roles 屬性和 permissions 屬性.
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
        info.addStringPermissions(permissionSet);
        //4. 返回 SimpleAuthorizationInfo 對象.
        return info;
    }
}

CustomRolesAuthorizationFilter數據庫

package com.wqqd.findfocus.shiro;

import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

/**
 * @author kexiang.bao
 * @create 2018-09-19 20:26
 */
public class CustomRolesAuthorizationFilter extends AuthorizationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest req, ServletResponse resp, Object mappedValue) throws Exception {
        Subject subject = getSubject(req, resp);
        String[] rolesArray = (String[]) mappedValue;

        if (rolesArray == null || rolesArray.length == 0) { //沒有角色限制,有權限訪問
            return true;
        }
        for (int i = 0; i < rolesArray.length; i++) {
            if (subject.hasRole(rolesArray[i])) { //若當前用戶是rolesArray中的任何一個,則有權限訪問
                return true;
            }
        }
        return false;
    }
}

FilterChainDefinitionMapBuilderapache

package com.wqqd.findfocus.shiro.factory;

/**
 * @author kexiang.bao
 * @create 2018-09-19 11:46
 */
import com.wqqd.findfocus.service.IMenuSysService;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.LinkedHashMap;

public class FilterChainDefinitionMapBuilder {
    @Autowired
    IMenuSysService iMenuSysService;
    public LinkedHashMap<String, String> buildFilterChainDefinitionMap(){
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
        map.put("/index.do", "anon");
        map.put("/validateMsgSend.do","anon");
        map.put("/login.do","anon");
        map.put("/logout.do","logout");
        map.put("/static/**","anon");
        map.putAll(iMenuSysService.getRoleMenuMap());
        map.put("/**", "authc");
        return map;
    }
}

至此就基本結束了。可是還有一個小坑。。就是當ajax請求若是超時或者沒有權限的時候,須要先後端稍微處理一下。後端

@RequestMapping(value = "/index.do")
    public String jsp(HttpServletRequest request,HttpServletResponse response){
        if (SecurityUtils.getSubject().isAuthenticated()){
            return "redirect:/welcome.do";
        }
        boolean isAjax = isAjaxRequest(request);
        if(isAjax) {
            response.setHeader("Session-Status", "timeout");
        }
        return "login";
    }

private boolean isAjaxRequest(HttpServletRequest request){
        Enumeration<String> values = request.getHeaders("X-Requested-With");
        while(values.hasMoreElements()) {
            String value = values.nextElement();
            if("XMLHttpRequest".equalsIgnoreCase(value)) {
                return true;
            }
        }
        return false;
    }

@RequestMapping(value = "/unauthorized.do")
    @ResponseBody
    public ServerResponse<String> unauthorized(HttpServletRequest request,HttpServletResponse response){
        boolean isAjax = isAjaxRequest(request);
        if(isAjax) {
            response.setHeader("Access-Status", "denied");
        }
        return ServerResponse.createByErrorMessage("您無權進行該操做");
    }

前端加上api

$(document).ajaxComplete(function(event, xhr, settings) {
        if (xhr.getResponseHeader('Session-Status') == 'timeout') {
            alert("您長時間未操做,請從新登陸");
            window.location.reload();
        } else if(xhr.getResponseHeader('Access-Status') == 'denied'){
            alert("您沒有權限進行該操做!請聯繫管理員");
            window.location.reload();
        }else if(403 == xhr.status) {
            window.location.reload();
        }
    });

至此就應該結束了,有問題再回來補充。

相關文章
相關標籤/搜索