一.shiro架构 组件
什么是shiro?
1.是Apache强大易用的开源安全框架,执行认证 授权 加密 会话管理等
2.与Spring Security相比 更简单 可脱离spring 颗粒度较粗
有哪些组件?
Subject:主体,可以看到主体可以是任何可以与应用交互的 “用户”;
SecurityManager:相当于 SpringMVC 中的 DispatcherServlet 或者 Struts2 中的 FilterDispatcher;是 Shiro 的心脏;所有具体的交互都通过 SecurityManager 进行控制;它管理着所有 Subject、且负责进行认证和授权、及会话、缓存的管理。
Authenticator:认证器,负责主体认证的,这是一个扩展点,如果用户觉得 Shiro 默认的不好,可以自定义实现;其需要认证策略(Authentication Strategy),即什么情况下算用户认证通过了;
Authrizer:授权器,或者访问控制器,用来决定主体是否有权限进行相应的操作;即控制着用户能访问应用中的哪些功能;
Realm:可以有 1 个或多个 Realm,可以认为是安全实体数据源,即用于获取安全实体的;可以是 JDBC 实现,也可以是 LDAP 实现,或者内存实现等等;由用户提供;注意:Shiro 不知道你的用户 / 权限存储在哪及以何种格式存储;所以我们一般在应用中都需要实现自己的 Realm;
SessionManager:如果写过 Servlet 就应该知道 Session 的概念,Session 呢需要有人去管理它的生命周期,这个组件就是 SessionManager;而 Shiro 并不仅仅可以用在 Web 环境,也可以用在如普通的 JavaSE 环境、EJB 等环境;所以呢,Shiro 就抽象了一个自己的 Session 来管理主体与应用之间交互的数据;这样的话,比如我们在 Web 环境用,刚开始是一台 Web 服务器;接着又上了台 EJB 服务器;这时想把两台服务器的会话数据放到一个地方,这个时候就可以实现自己的分布式会话(如把数据放到 Memcached 服务器);
SessionDAO:DAO 大家都用过,数据访问对象,用于会话的 CRUD,比如我们想把 Session 保存到数据库,那么可以实现自己的 SessionDAO,通过如 JDBC 写到数据库;比如想把 Session 放到 Memcached 中,可以实现自己的 Memcached SessionDAO;另外 SessionDAO 中可以使用 Cache 进行缓存,以提高性能;
CacheManager:缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据基本上很少去改变,放到缓存中后可以提高访问的性能
Cryptography:密码模块,用于如密码加密 / 解密的。
二.shiro 认证 授权
1.shiro认证:
创建SecurityManager环境------->主体提交认证-------->SecurityManager认证----->Authenticator认证----->Realm验证
实现流程:
1.1引入jar包
1.2.创建一个测试类
public class ShiroTest {
SimpleAccountRealm sa=new SimpleAccountRealm();
@Before
public void addUser(){
sa.addAccount("Tom", "1234567");
}
@Test
public void AuthenticationTest(){
//创建环境
DefaultSecurityManager ds=new DefaultSecurityManager();
ds.setRealm(sa);
//主体提交认证请求
SecurityUtils.setSecurityManager(ds);
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken user=new UsernamePasswordToken("Tom", "1234567");
subject.login(user);
System.out.println("认证:"+subject.isAuthenticated());
subject.logout();
System.out.println("认证退出:"+subject.isAuthenticated());
}
}
1.3 加密
这里使用MD5加密 ,MD5是一种不可逆的加密算法,所谓的破解,不是将摘要还原成原文,实际上属于一种碰撞.MD5碰撞的方法有很多包括:暴力枚举法 字典法 彩虹表法.
暴力枚举就是找出所有的排列组合,所以时间复杂度极高,例如由数字字母大小写组成的8位密码,就存在的组合约为200万亿,但是会优先尝试生日等有意义的单词组合.
字典法:储存原文和对应的哈希值,需要极大的空间,8位的约4.65PB的空间
所以彩虹表法是比较可取的,采用了H(x)和R(x)两个基本函数
H(x):生成信息摘要的哈希函数,比如MD5 SHA256
R(x):从信息摘要转换成另一串字符串的衰减函数,其中R(x)的定义域是H(x)的值域,R(x)的值域是H(x)的定义域,但不是反函数关系.
通过交替计算H和R若干次(K次),可以形成一个原文和哈希值得链条,长度为2K+1,只需把首端和尾端存入哈希表中,一组哈希表代表了一组映射关系,但是也会存在缺陷:R(x)的可靠性.可能导致末端的值一致,但是在链条中找不到匹配的值.
R(920ECF10)=kiebgt 和 R(FB107E70)=Kiebgt 或者可能在中间的R(x)发生错误,那么后面的一截全是一样的,导致重复.避免的方式是采用将原来的R(x)改进成R1(x) 到Rk(x),一共k个衰减函数,发生碰撞的可能就只会在同一级运算.
MD5因为加密后的字符串是固定的,所以不管加密多少次都是同样的结果
实现方式有很多种,这里介绍两种:
1.new Md5Hash("加密的值").toString();
2.DigestUtils.md5Hex("加密的值");
1.4 HashedCredentialsMatcher的配置
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!-- 加密方式 -->
<property name="hashAlgorithmName" value="MD5" />
<!-- 加密次数 -->
<property name="hashIterations" value="2" />
<!-- 存储散列后的密码是否为16进制 -->
<property name="storedCredentialsHexEncoded" value="true" />
</bean>
HashedCredentialsMatcher配置的属性值要跟加密时的属性(hashAlgorithmName,hashIterations,storedCredentialsHexEncoded)一致
public class ShiroTest {
SimpleAccountRealm sa=new SimpleAccountRealm();
@Before
public void addUser(){
sa.addAccount("Tom",new Md5Hash("1234567").toString());
}
@Test
public void AuthenticationTest(){
//创建环境
DefaultSecurityManager ds=new DefaultSecurityManager();
ds.setRealm(sa);
HashedCredentialsMatcher h=new HashedCredentialsMatcher();
//加密名称
h.setHashAlgorithmName("md5");
//加密次数
h.setHashIterations(2);
sa.setCredentialsMatcher(h);
//主体提交认证请求
SecurityUtils.setSecurityManager(ds);
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken user=new UsernamePasswordToken("Tom", "1234567");
subject.login(user);
System.out.println("认证:"+subject.isAuthenticated());
subject.logout();
System.out.println("认证退出:"+subject.isAuthenticated());
}
}
2.授权
跟上面的一样,只需要添加一个角色在后面或者多个角色 然后checkRoles检查是否有这个角色或多个角色.
三.shiro的自定义realm
1.继承AuthorizingRealm 然后实现认证和授权两个方法
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//1.从主体传过来的认证信息中获取用户名
String useName=(String) token.getPrincipal();
//2.通过用户名到数据库中获取用户凭证
String password=getPasswordByName(useName);
if(password==null){
return null;
}
SimpleAuthenticationInfo sa=new SimpleAuthenticationInfo("Tom",password,"CustomRealm");
return sa;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String userName=(String) principals.getPrimaryPrincipal();
//拿到角色
Set<String> roles=getRolesByName(userName);
//拿到权限
Set<String> permissions=getPermissionsByName(userName);
SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
authorizationInfo.setStringPermissions(permissions);
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
public class CustomRealmTest {
CustomRealm custom=new CustomRealm();
@Test
public void realmTest (){
//创建环境
DefaultSecurityManager ds=new DefaultSecurityManager();
ds.setRealm(custom);
//主体提交认证请求
SecurityUtils.setSecurityManager(ds);
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken user=new UsernamePasswordToken("Tom", "123");
subject.login(user);
System.out.println("认证:"+subject.isAuthenticated());
subject.checkRole("admin2");
subject.checkPermissions("update","add");
}
}
四.集成Spring
1.加入jar包
2.shiro也提供了一个shiroFilter 放入web.xml中
<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>
3. 建立Spring-shiro.xml
<!-- Realm实现 -->
<bean id="userRealm" class="com.jumper.record.stations.auth.AuthenticationRealm">
<property name="cachingEnabled" value="false"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
</bean>
<!-- Shiro的Web过滤器 -->
<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="audit.html"/>
<!-- 没有权限时跳转的页面 -->
<property name="unauthorizedUrl" value="unauthorized.html"/>
<!-- 有顺序的从上往下匹配 -->
<property name="filterChainDefinitions">
<value>
/login.html = authc
/* = authc
</value>
</property>
</bean>
4.实现类
用注解注入
1.加入jar包
2.加入配置
<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"></property>
</bean>
3. 实现类
内置的过滤器:
认证: auon 不需要任何的认证;user 当前存在用户 ;logout 退出; authc 需要认证后才可以访问
授权: ssl安全协议;port [""] 端口是括号内存在的;roles[""] 角色是括号内存在的;perms[""] 具备括号内存在的才可以访问
自置过滤器:
1.写好一个自定义的过滤器
2.引入spring-shiro.xml
3.放入shiroFilter
shiro入门新手,不足的地方还请多多指教~