shiro的url配置采用第一次匹配优先的原则,则从上往下匹配
DelegatingFilterProxy类存在与spring-web包中,其作用就是一个filter的代理,用这个类的好处是可以通过spring容器来管理filter的生命周期,还有就是,可以通过spring注入的形式,来代理一个filter执行,如shiro。
<!--
配置shiro的 shiro Filter
1.DelegatingFilterProxy类是Spring的一个filter的代理对象
-->
<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>
<!-- 可以用targetBeanName初始化参数来指定filter名 -->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter1</param-value>
</init-param>
</filter>
初始化时,自动调用init方法。
Shiro认证思路。
//Handler类
@Controller
@RequestMapping("/shiro")
public class ShiroHandle {
@RequestMapping("/login")
public String login(@RequestParam("username")String username,@RequestParam("password")String password){
Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
System.out.println(token.hashCode());
token.setRememberMe(true);
try {
currentUser.login(token);
}
catch (AuthenticationException ae) {
ae.printStackTrace();
}
}
return "";
}
}
//shiroRealm类
public class ShiroRealm extends AuthenticatingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("doAuthentication"+token.hashCode());
return null;
}
}
Handler类中执行的login方法传入的token值会被传到shiroRealm类里面。两个token实为同一个对象。
Shiro密码的比对是由 AuthenticatingRealm 类的assertCredentialsMatch方法使用CredentialsMatcher来进行比对的。
<!--
配置realm
实现Realm接口
-->
<bean id="jdbcRealm" class="com.jun.shiro.ShiroRealm">
<!--
因为shiro是使用AuthenticatingRealm的credentialsMatcher属性来进行密码比对,
而此Realm是继承了AuthenticatingRealm,所以把credentialsMatcher属性注入到bean里面,
这里使用其实现类HashedCredentialsMatcher,并指定加密算法为MD5,加密次数为1024次
-->
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 指定加密算法 -->
<property name="hashAlgorithmName" value="MD5"></property>
<!-- 指定加密次数 -->
<property name="hashIterations" value="1024"></property>
</bean>
</property>
</bean>
shiro默认的认证策略是AllLeastOneSuccessfulStrategy。
若需要实现多realm认证的话,需要在spring中注册ModularRealmAuthenticator类,并且作为参数赋给securityManager。
<!--
配置securityManager
-->
<bean id="securityManager"
class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<!-- <property name="realm" ref="jdbcRealm"/> -->
<property name="authenticator" ref="authenticator"></property>
</bean>
<!-- 多realm认证器 -->
<bean class="org.apache.shiro.authc.pam.ModularRealmAuthenticator"
id="authenticator">
<property name="realms">
<list>
<ref bean="jdbcRealm"/>
<ref bean="secondRealm"/>
</list>
</property>
<!-- 配置多Realm认证策略 -->
<property name="authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
</property>
</bean>
但在实际生产中,我们是从securityManager读取realm的,所以把realms从securityManager注入。那为什么可以这样做呢?是因为在securityManager初始化的时候,会判断authenticator属性对象是否属于ModularRealmAuthenticator类,如果是,会将自身realms属性赋予authenticator属性对象。
<!--
配置 securityManager
-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<!-- <property name="realm" ref="jdbcRealm"/> -->
<property name="authenticator" ref="authenticator"></property>
<property name="realms">
<list>
<ref bean="jdbcRealm"/>
<ref bean="secondRealm"/>
</list>
</property>
</bean>
<!-- 多realm认证器 -->
<bean class="org.apache.shiro.authc.pam.ModularRealmAuthenticator" id="authenticator">
<!-- 配置多Realm认证策略 -->
<property name="authenticationStrategy">
<bean class="org.apache.shiro.authc.pam.AllSuccessfulStrategy"></bean>
</property>
</bean>
AuthenticatingRealm-------->若只需要认证的话,只继承AuthenticatingRealm就可
AuthorizingRealm--------->用于授权和认证的realm一般使用这个
AuthorizingRealm继承于AuthenticatingRealm但是没有实现父类的doGetAuthenticationInfo方法
若为多realm授权认证的话,shiro会调用ModularRealmAuthorizer类来进行认证(从源码中可以看到,只要有一个Realm通过就返回true),但是实际进行授权认证的还是AuthorizingRealm,因为在源码374行中调用的hasRole方法会继续调用AuthorizingRealm授权认证。
sessionDAO
sessionDao可以讲session进行序列号并存到数据库中,进行增删改查。在单机应用中,作用可以是在service层也可以通过shiro拿到session。而在集群分布式中作用就会比较大,共享session很有必要,因为当同一个用户发起请求时,可能被调度到不同的机器上访问(负载均衡),这时就可能同一个用户的求情会被当成是多个用户的请求,session共享就很有必要了。
の 面试题
-
shiro的优点
- 与Spring Security相比,Spring Security需要依赖与Spring环境,而shiro组件化,简单易用,不用依赖其他框架。
- 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等)
- 内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境
- 对角色的简单的签权(访问控制),支持细粒度的签权
- 支持一级缓存,以提升应用程序的性能
2.为什么shiro的注解一般都加在control层,而不是在service层。
因为一般在项目中我们会在service层会加上@Transactional 事务注解,这个时候service层对象已经是个代理对象了,在加上shiro权限注解的话,代理的代理,会在注入的时候发生异常。