天天看點

shiro權限架構

shiro權限架構

1.shiro依賴包

<!-- 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>  
</dependencies>
           

2.在web.xml中配置shiro代理過濾器

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

3.spring配置

<?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.xsd">
    <!--配置安全管理者-->
    <bean class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" id="securityManager">
        <!--配置安全管理者的權限資料-->
        <property name="realm" ref="myRealm"/>
    </bean>
    <bean class="com.wal.aisell.shiro.MyRealm" id="myRealm">
    //設定密碼的比對方式
        <property name="credentialsMatcher">
            <bean id="credentialsMatcher"  class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
                <!--設定加密方式-->
                <property name="hashAlgorithmName" value="MD5"/>
                <property name="hashIterations" value="10"/>
            </bean>
        </property>
    </bean>


    <!--配置shiro生命周期方法-->
    <bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor" id="lifecycleBeanPostProcessor"/>
    <!--配置使用shiro的注解,但是必需配置在Spring Ioc容器中Shiro bean的生成周期方法-->
    <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>

    <!--配置真實的shiro過濾器 必須與web.xml中的代理過濾器名字一緻-->
    <bean class="org.apache.shiro.spring.web.ShiroFilterFactoryBean" id="shiroFilter">
        <property name="securityManager" ref="securityManager"/>
        <!--登入時的url,如果沒有登入會自動跳到該頁面-->
        <property name="loginUrl" value="/login"/>
        <!--登入成功後跳到的頁面-->
        <property name="successUrl" value="/main"/>
        <!--沒有權限是跳到這個頁面-->
        <property name="unauthorizedUrl" value="/s/unauthorized.jsp"/>


        <!--配置哪些資源被保護,哪些資源需要權限
            anon:不需要登入也可以通路相應的權限
            authc:需要權限才能通路
              ** :所有檔案及其子檔案
        -->
        <!--<property name="filterChainDefinitions">
            <value>
                /s/login.jsp=anon
                /login=anon
                /s/main.jsp=perms[user:*]
                \/**=authc
            </value>
        </property>-->
        <!--資源權限map-->
        <property name="filterChainDefinitionMap" ref="filterChainDefinitionMap"/>
        <!--引入自定義權限過濾器-->
        <property name="filters">
            <map>
                <entry value-ref="myPermissionAuthorizationFilter" key="wal"></entry>
            </map>
        </property>
    </bean>
    <!--引入自定義權限過濾器bean-->
    <bean class="com.wal.aisell.shiro.MyPermissionAuthorizationFilter" id="myPermissionAuthorizationFilter"/>
     <!--在bean中設定哪些資源需要放行,哪些資源需要權限-->
    <bean class="com.wal.aisell.shiro.FilterChainDefinitionMapBuilder" id="filterChainDefinitionMapBuilder"></bean>
    <bean id="filterChainDefinitionMap" factory-bean="filterChainDefinitionMapBuilder"
          factory-method="creatFilterChainDefinitionMap"/>

</beans>
           

資源權限MapfilterChainDefinitionMapBuilder

public class FilterChainDefinitionMapBuilder {
    @Autowired
    private IPermissionService permissionService;
    public Map<String,String> creatFilterChainDefinitionMap(){
        LinkedHashMap<String, String> map = new LinkedHashMap<>();
       /* /s/login.jsp=anon
                /login=anon
                /s/main.jsp=perms[user:*]
        *//**=authc*/
        //靜态資源放行
        map.put("/login","anon");
        map.put("*.js","anon");
        map.put("*.css","anon");
        map.put("/css/**","anon");
        map.put("/js/**","anon");
        map.put("/easyui/**","anon");
        map.put("/images/**","anon");
        //從資料庫中讀出通路路徑所需要的權限
        List<Permission> permissionList = permissionService.findAll();
        permissionList.forEach(permission -> {
            String url = permission.getUrl();
            String sn = permission.getSn();
            //這裡要改成自定義的權限過濾器對應的key值
            map.put(url, "wal["+sn+"]");
        });
            map.put("//**","authc" );
        return map;
    }
}
           

自定義權限過濾器 根據使用者權限做相應處理

//複寫預設權限過濾器,使其支援ajax請求
public class MyPermissionAuthorizationFilter extends PermissionsAuthorizationFilter {
    //這是請求沒有權限時處理的方法,預設是跳轉頁面,現在要加一個對ajax的處理
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        Subject subject = this.getSubject(request, response);
        if (subject.getPrincipal() == null) {
            this.saveRequestAndRedirectToLogin(request, response);
        } else {
            //先強轉成http請求,因為http請求才能獲得請求頭
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            String header = req.getHeader("X-Requested-With");
            //ajax請求的标志XMLHttpRequest
            if (header != null && "XMLHttpRequest".equals(header)){
                //設定響應類型
                resp.setContentType("text/json,charset=UTF-8");
                //設定響應消息
                resp.getWriter().print("{\"seccess\":false,\"msg\":\"沒有權限\"}");
            }else {
                String unauthorizedUrl = this.getUnauthorizedUrl();
                if (StringUtils.hasText(unauthorizedUrl)) {
                    WebUtils.issueRedirect(request, response, unauthorizedUrl);
                } else {
                    WebUtils.toHttp(response).sendError(401);
                }
            }
        }

        return false;
    }
}
           

自定義MyRealm 使用者身份認證和權限管理

public class MyRealm extends AuthorizingRealm {
    //驗證角色和權限
    @Autowired
    private IPermissionService permissionService;
    @Autowired
    private IRoleService roleService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        //拿到身份驗證成功後傳回的主題
        Employee employee = (Employee)principalCollection.getPrimaryPrincipal();
        //更具登入使用者得到該使用者的角色資訊
        Set<Role> roles = roleService.findRolesByLoginUser(employee);

        Set<String> rolesStr = new HashSet<>();
        Set<String> permissionAll = new HashSet<>();
        roles.forEach(role -> {
            //根據登入使用者的角色資訊,拿到使用者權限
            Set<String> permission = permissionService.findPermissionByRole(role);
            permissionAll.addAll(permission);
            String sn = role.getSn();
            rolesStr.add(sn);

        });
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setStringPermissions(permissionAll);
        authorizationInfo.setRoles(rolesStr);
        //Set<String> permission = permissionService.findPermissionByLoginUser(employee);
        //SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        //authorizationInfo.setStringPermissions(permission);
        return authorizationInfo;
    }
    //驗證身份
    @Autowired
    private IEmployeeService employeeService;
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //authenticationToken對應subject.login(token) 中的token
        String username = (String)authenticationToken.getPrincipal();
        String password = employeeService.findPasswordByusername(username);
        Employee employee = employeeService.findByusername(username);
        if (password==null){
            return null;
        }
        //設定鹽值
        ByteSource salt = ByteSource.Util.bytes(MD5Utils.SALT);
        //密碼比對成功後 ,subject.getPrincipal獲得的就是employee,如果你寫的是usernamesubject獲得的就是username
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(employee, password,salt, getName());
        return authenticationInfo ;
    }