一、shiro简介
shiro是Java的一个安全(权限框架)。
shiro可以完成:认证、授权、加密、会话管理、与web集成、缓存等;
最主要的四个功能:认证(如密码匹配)、授权、会话管理、加密;
**Subject:**即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。但考虑到大多数目的和用途,你可以把它认为是Shiro的“用户”概念。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
**SecurityManager:**它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
二、HelloWorld
shiroFilter是一个入口;
anon 拦截器表示匿名访问,既不需要登录即可访问;
authc 拦截器表示需要身份认证通过后才能进行访问;
logout 表示登出的过滤器
没有配置成 authc 的可以进行访问;配置成 anon 匿名的可以进行访问;
<property name="filterChainDefinitions">
<value>
/login.jsp = anon
/** = authc
</value>
</property>
在 applicatioContext.xml 中配置的 org.apache.shiro.spring.web.ShiroFilterFactoryBean 的名字必须和 web.xml 中的 org.springframework.web.filter.DelegatingFilterProxy 的名字相同;若不一致,则会在加载项目时抛出
org.springframework.beans.factory.NoSuchBeanDefinitionException 异常,因为 shiro 会在 IOC 容器中 查找和 <filter-name>
相同的对象的bean(也可以通过 targetBeanName 这个参数配置);
DelegatingFilterProxy 实际上是 Filter 的一个代理对象,默认情况回到 IOC 容器中查找和 <filter-name>
对应的filter bean,也可以通过 targetBeanName 的初始化参数来配置 filter bean 的id;
URL 匹配模式使用 Ant 风格模式:
?:匹配一个字符;
*:匹配零个或多个字符;
**:匹配路径中的另个或多个路径;
URL匹配采用第一匹配优先的模式;
shiro认证:
1. 获取当前的Subject,调用 SecurityUtils.getSubject();
2.测试当前的用户是否已经被认证,即是否已经登录, 调用 Subject 的 isAuthenticates()
3.若没有被认证,则把用户名和密码封装为 UsernamePasswordToken对象;
1).创建一个表单页面
2).把请求提交到 SpringMVC的Handler
3).获取用户名和密码
4.执行登录:调用Subject 的login(AuthenticationToken )方法。
5.自定义 Realm 的方法,从数据库中获取对应的记录,返回给 shiro;
1).实际上需要继承 org.apache.shiro.realm.AuthenticatingRealm 类
2).实现doGetAuthenticationInfo(AuthenticationToken) 方法。
6.由 shiro 完成对密码的比对。
自定义Realm:
package com.vincent.shiro.realms;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.realm.AuthenticatingRealm;
public class ShiroRealm extends AuthenticatingRealm{
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken token) throws AuthenticationException {
//1、把 AuthenticationToken 转换为 UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、从 UsernamePasswordToken 中获取 username
String username = upToken.getUsername();
//3、调用数据库的方法,从数据库中查询 username 对应的用户记录
System.out.println("从数据库中获取username:"+username+" 所对应的用户信息。");
//4、若用户不存在,则可以抛出 UnknownAccountException 异常
if("unknown".equals(username)){
throw new UnknownAccountException("用户不存在");
}
//5、根据用户信息的情况,决定是否需要抛出其他的 AuthenticationException 异常
if("monster".equals(username)){
throw new LockedAccountException("用户被锁定");
}
//6、根据用户的情况,来构建 AuthenticationException 对象并返回。通常使用的实现类为:SimpleAuthencationInfo
//以下信息是从数据中获取的
//(1)、principle:认证的实体信息,可以是username,也可以是数据表对应的用户的实体类对象.
Object principal = username;
//(2)、credentials:从数据表中获取的密码
Object credentials = "123456";
//(3):realmName:当前 realm 对象的 name,调用父类的 getName() 方法即可
String realmName = getName();
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal, credentials, realmName);
return info;
}
}
tips:由于存在shiro 缓存,因此需要在登录成功后需要执行登出操作才能测试其他效果;
密码的比对:
通过 AuthenticationRealm 的credentialsMatcher 属性来进行的密码的比对!可以通过 credentialsMatcher 来进行密码的加密。
密码的加密(MD5加密、SHA1加密):
在数据库中保存的密码不应该是明文,而且加密操作应该是不可逆的;
1)、如何把一个字符串加密为 MD5(对从数据库中获取的字符串进行加密);
2)、替换当前 Realm 的credentialsMatcher 。直接使用HashedCredentialsMatcher 对象,并设置属性加密算法即可(用于把从前台获取的密码进行 MD5 加密);
<bean id="jdbcRealm" class="com.vincent.shiro.realms.ShiroRealm">
<property name="credentialsMatcher">
<bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="MD5"></property>
</bean>
</property>
</bean>
密码的MD5盐值加密(salt):
密码的MD5加密存在的问题:如果两人使用的密码一样,则加密后的结果将会一样;
步骤:
1):在 doGetAuthenticationInfo 方法返回值创建 SimpleAuthenticationInfo 对象时,需要使用如下构造器:
new SimpleAuthenticationInfo(principal, credentials, credentialsSalt, realmName)
2)、使用 ByteSource.Util.bytes() 计算盐值;
3)、盐值需要唯一:一般使用随机字符串或 user id;
4)、使用 new SimpleHash(algorithmName, source, salt, iterations); 来计算盐值加密后的密码的值;
使用以上方法,即使两个人使用同样的密码,数据库中保存的密码也不相同;
多Realm:
配置流程:
<bean id="authenticator" class="org.apache.shiro.authc.pam.ModularRealmAuthenticator">
<property name="realms">
<list>
<ref bean="jdbcRealm"/>
<ref bean="secondRealm"/>
</list>
</property>
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="cacheManager" ref="cacheManager"/>
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="authenticator" ref="authenticator"/>
</bean>
多个realm是有认证顺序的;