Shiro执行流程 :
spring配置文件==>Subject==>安全管理器SecurityManager==>Realm
Shiro拦截方式 :
1.URL拦截(常用)
2.注解方式拦截(常用)
3.标签拦截
4.编码判断拦截
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-allartifactId>
<version>${shiro.version}version>
dependency>
注意 : 如果使用struts,需要配置在struts过滤器前面
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
anon 未认证可以访问
authc 认证后可以访问
perms 需要特定权限才能访问
roles 需要特定角色才能访问
user 需要特定用户才能访问
port 需要特定端口才能
注意:需要将spring的事务改成cglib代理,否则权限注解不起作用
<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">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/login.html" />
<property name="successUrl" value="/index.html" />
<property name="unauthorizedUrl" value="/unauthorized.html" />
<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" />
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" >
<property name="proxyTargetClass" value="true">property>
bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
bean>
beans>
@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;
}
授权 : 在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 roles = roleService.findRolesByUser(user);
for (Role role : roles) {
info.addRole(role.getKeyword());
}
// 根据用户id查询对应权限
List 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
spring的事务必须是cglib代理
proxy-target-class=”true”
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"
depends-on="lifecycleBeanPostProcessor" >
<property name="proxyTargetClass" value="true">property>
bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager" />
bean>
例如:
@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]
问题: 为什么使用 ehcache 而不使用 redis 缓存
1、Shiro 默认对 ehcache 的支持
2、在后台管理系统中 ehcache 使用非常普遍
<dependency>
<groupId>net.sf.ehcachegroupId>
<artifactId>ehcache-coreartifactId>
<version>2.6.11version>
dependency>
<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>
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache.xml"/>
bean>
<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类为实现序列化接口异常
运行即可