Shiro安全框架的使用

标签: 权限框架  shiro

Shiro执行流程 :
spring配置文件==>Subject==>安全管理器SecurityManager==>Realm
Shiro拦截方式 :
1.URL拦截(常用)
2.注解方式拦截(常用)
3.标签拦截
4.编码判断拦截

1.在maven中添加坐标

<!-- 权限控制 框架 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-all</artifactId>
    <version>${shiro.version}</version>
</dependency>

2.在web.xml中配置Shiro过滤器

注意 : 如果使用struts,需要配置在struts过滤器前面

<!-- 配置shiro拦截器 -->
    <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>

4.在spring配置文件中配置

anon 未认证可以访问
authc 认证后可以访问
perms 需要特定权限才能访问
roles 需要特定角色才能访问
user 需要特定用户才能访问
port 需要特定端口才能
注意:需要将spring的事务改成cglib代理,否则权限注解不起作用

<?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:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:jpa="http://www.springframework.org/schema/data/jpa" xmlns:task="http://www.springframework.org/schema/task"
    xmlns:jaxrs="http://cxf.apache.org/jaxrs"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/data/jpa 
        http://www.springframework.org/schema/data/jpa/spring-jpa.xsd
        http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd ">

    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!-- shiro 的核心安全接口 -->
        <property name="securityManager" ref="securityManager" />
        <!-- 要求登录时的链接 -->
        <property name="loginUrl" value="/login.html" />
        <!-- 登陆成功后要跳转的连接 -->
        <property name="successUrl" value="/index.html" />
        <!-- 未授权时要跳转的连接 -->
        <property name="unauthorizedUrl" value="/unauthorized.html" />
        <!-- shiro 连接约束配置 -->
        <property name="filterChainDefinitions">
            <value>
                /login.html* = anon
                /userAction_login.action* = anon
                /css/** = anon
                /js/** = anon
                /images/** = anon
                /attached/** = anon
                /upload/** = anon
                /services/* = anon
                /edit/** = anon
                /validatecode.jsp = anon
                /pages/base/courier.html* = perms[courier:list]
                /pages/base/area.html* = roles[base]
                /** = authc
            </value>
        </property>
    </bean>
    <!-- 配置 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="bosRealm" />
    </bean>
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
    <!-- 开启Shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
        depends-on="lifecycleBeanPostProcessor" >
        <!-- 必须使用cglib代理 -->
        <property name="proxyTargetClass" value="true"></property>  
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>

</beans>

5.代码演示(登陆)

action :
@Action(value = "userAction_login")
    public String login() throws Exception {

        Subject subject = SecurityUtils.getSubject();
        AuthenticationToken token = new UsernamePasswordToken(entity.getUsername(), entity.getPassword());

        try {
            subject.login(token);
            java2Json(BosResult.ok(), new String[] {});
        } catch (UnknownAccountException e) {
            e.printStackTrace();
            java2Json(BosResult.build(400, "账号不存在~"), new String[] {});
        } catch (IncorrectCredentialsException e) {
            e.printStackTrace();
            java2Json(BosResult.build(400, "密码错误~"), new String[] {});
        }
        return NONE;
    }
realm : 需要注入给SecurityManager

授权 : 在applicationContext.xml中配置的权限和角色定的路径拦截

import java.util.List;

import org.apache.shiro.SecurityUtils;
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.authc.UsernamePasswordToken;
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.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.itcast.bos.domain.system.Permission;
import cn.itcast.bos.domain.system.Role;
import cn.itcast.bos.domain.system.User;
import cn.itcast.bos.service.delivery.PermissionService;
import cn.itcast.bos.service.delivery.RoleService;
import cn.itcast.bos.service.delivery.UserService;

/**
 * 安全管理 Realm
 * 
 * @author Administrator
 *
 */
@Service
public class BosRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Override
    // 授权方法
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        System.out.println("Shiro 授权执行...");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 获取用户信息
        Subject subject = SecurityUtils.getSubject();
        User user = (User) subject.getPrincipal();
        // 根据用户id查询对应角色
        List<Role> roles = roleService.findRolesByUser(user);
        for (Role role : roles) {
            info.addRole(role.getKeyword());
        }
        // 根据用户id查询对应权限
        List<Permission> permissions = permissionService.findPermissionByUser(user);
        for (Permission permission : permissions) {
            info.addStringPermission(permission.getKeyword());
        }
        return info;
    }

    @Override
    // 认证方法
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authToken) throws AuthenticationException {
        System.out.println("Shiro 认证执行...");
        // 获取页面传来的username和password
        UsernamePasswordToken token = (UsernamePasswordToken) authToken;
        // 调用业务层查询执行查询
        User user = userService.findByUsername(token.getUsername());
        if (user == null) {
            return null;
        }
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
    }
}
结果:密码和用户名错误会抛出异常 ,需要对异常进行处理
    用户名不存在:
        UnknownAccountException
    密码错误:
        IncorrectCredentialsException

6.权限注解的使用(细颗粒度权限控制)

1.在spring配置文件中激活注解权限

spring的事务必须是cglib代理
proxy-target-class=”true”

    <!-- 开启Shiro注解 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" 
        depends-on="lifecycleBeanPostProcessor" >
        <!-- 必须使用cglib代理 -->
        <property name="proxyTargetClass" value="true"></property>  
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager" />
    </bean>
2.在方法上注解
例如:
 @Override
    @RequiresPermissions("courier_add")
    public void saveCourier(Courier entity) {
        entity.setDeltag('0');// 0为正常, 1为标记作废
        courierRepository.save(entity);
    }
使用方法注解进行权限控制, 当权限不足时,代理对象抛出一个异常,需要对异常捕捉
org.apache.shiro.authz.UnauthorizedException: Subject does not
have permission [courier_add]
3.常用注解

这里写图片描述

7. 给权限添加缓存(EhCache)

问题: 为什么使用 ehcache 而不使用 redis 缓存
1、Shiro 默认对 ehcache 的支持
2、在后台管理系统中 ehcache 使用非常普遍

1.引入ehCache的maven坐标
<!-- 引入ehCache依赖 -->
<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache-core</artifactId>
    <version>2.6.11</version>
</dependency>
2.复制ehcache jar包下的ehcache.xml文件
    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">

    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <cache name="promissionCache"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU">
        <persistence strategy="localTempSwap"/>
    </cache>
</ehcache>
3. 在spring配置文件中配置
    <!-- 配置ehCache -->
    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <!-- 指定ehcache.xml文件的位置-->
        <property name="configLocation" value="classpath:ehcache.xml"/>
    </bean>

    <!-- 配置shiro封装ehCache -->
    <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManager" ref="ehCacheManager"/>
    </bean>

    <bean id="bosRealm" class="cn.itcast.bos.realm.BosRealm">
        <!--注入缓存区自定义的名称-->
        <property name="authorizationCacheName" value="promissionCache"/>
    </bean>

注:最后将shiroCacheManager注入给SecurityManager
特别注意:被缓存的对象要实现序列化接口,否则会报xx类为实现序列化接口异常
运行即可

版权声明:本文为xxs5258原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/xxs5258/article/details/81416712