什么是权限管理
权限管理是指按照安全策略对于用户可访问的资源进行管控,其主要包含两个部分:
用户认证以及用户授权。
用户认证
用户访问系统,系统需要检验用户身份的合法性。
常见的用户验证方法有:
- 用户名与密码
- 指纹打卡机
基于证书验证
在用户认证环节中,主要有三个关键对象:
Subject
- Principal
- Credential
用户授权
用户授权,又可以称之为访问控制,在用户认证通过后,系统对于用户访问的资源及逆行控制,用户具有资源的访问权限方可访问。
权限模型
六表模型
实际开发模型
初探访问控制
基于URL进行权限管控
实现思路:
将系统操作的每个URL配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过滤,过滤器获取到用户访问的URL,若系统访问的URL是用户所分配的角色中的URL则放行。
Shiro 权限管控
Shiro提供了应用程序安全API来执行以下几个方面:
- 身份验证 - 验证用户身份,通常被称之为用户“登录”
- 授权- 访问控制
- 加密- 保护或隐藏窥探数据
- 会话管理 - 用户登陆之后就是一次会话,在未退出之前,其所有的信息都保存在会话之中。
Shiro会以这两种方式对权限进行把控:
- 基于角色的访问控制
- 基于资源的访问控制
Shiro的API主要有以下几个:
Subject:主体,负责与用户交互
- SecurityManager:安全管理器,对所有的subject进行安全管理
- Authenticator:认证器,负责对用户身份进行认证
- Authorizer:授权器
- Realm:领域,相当于datasource数据源。
- SessionManager:会话管理
- SessionDAO:会话Dao
- CacheManager:缓存管理
- Cryptography:密码管理
Shiro过滤器:
过滤器简称 | 对应的 Java 类 |
---|---|
anon | org.apache.shiro.web.filter.authc.AnonymousFilter |
authc | org.apache.shiro.web.filter.authc.FormAuthenticationFilter |
authcBasic | org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter |
perms | org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter |
port | org.apache.shiro.web.filter.authz.PortFilter |
rest | org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter |
roles | org.apache.shiro.web.filter.authz.RolesAuthorizationFilter |
ssl | org.apache.shiro.web.filter.authz.SslFilter |
user | org.apache.shiro.web.filter.authc.UserFilter |
logout | org.apache.shiro.web.filter.authc.LogoutFilter |
noSessionCreation | org.apache.shiro.web.filter.session.NoSessionCreationFilter |
/admins/=anon # 表示该 uri 可以匿名访问
/admins/=auth # 表示该 uri 需要认证才能访问
/admins/=authcBasic # 表示该 uri 需要 httpBasic 认证
/admins/=perms[user:add:] # 表示该 uri 需要认证用户拥有 user:add: 权限才能访问
/admins/=port[8081] # 表示该 uri 需要使用 8081 端口
/admins/=rest[user] # 相当于 /admins/=perms[user:method],其中,method 表示 get、post、delete 等
/admins/=roles[admin] # 表示该 uri 需要认证用户拥有 admin 角色才能访问
/admins/=ssl # 表示该 uri 需要使用 https 协议
/admins/=user # 表示该 uri 需要认证或通过记住我认证才能访问
/logout=logout # 表示注销,可以当作固定配置
anon,authcBasic,auchc,user 是认证过滤器。
perms,roles,ssl,rest,port 是授权过滤器。
Shiro常规流程
加载配置文件-》获取安全管理实例-》将管理对象放入全局工具类中-》通过工具类生产Subject对象-》将用户数据封装成Token-》将Token注入到subject之中-》使用Realm判断相关权限。
public class MyFirstTest {
@Test
public void test1() {
//加载配置文件,并获取工厂,这个SecurityManager小心与java.lang包下的SecurityManager冲突
Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_1.ini");
//获取安全管理者实例
org.apache.shiro.mgt.SecurityManager sm = factory.getInstance();
//将安全管理者放入全局对象
SecurityUtils.setSecurityManager(sm);
//全局对象通过安全管理者生成Subject对象
Subject subject = SecurityUtils.getSubject();
//封装用户的数据
UsernamePasswordToken token = new UsernamePasswordToken("jay", "123");
//将用户的数据token 最终传递到Realm中进行对比
subject.login(token);
//判断本帐号是否已经被认证
Assert.assertEquals(true, subject.isAuthenticated());
}
}
加密Demo
/**
* 加密算法
*/
public class MD5Test {
public static void main(String[] args) {
//原始 密码
String source = "111111";
//盐
String salt = "qwerty";
//散列次数(和ini中credentialsMatcher.hashIterations=1一致)
int hashIterations = 1;
//上边散列1次:f3694f162729b7d0254c6e40260bf15c
//上边散列2次:36f2dfa24d0a9fa97276abbe13e596fc
//第一个参数:散列算法
//这里的散列算法是md5要和ini中credentialsMatcher.hashAlgorithmName=md5一致)
//第二个参数:明文,原始密码
//第三个参数:盐,通过使用随机数
//第四个参数:散列的次数,比如散列两次,相当 于md5(md5(''))
SimpleHash simpleHash = new SimpleHash("md5", source, salt, hashIterations);
System.out.println(simpleHash.toString());
}
}
//这个结果:f3694f162729b7d0254c6e40260bf15c就是在自定义realm中的密文也就是该用户保存到数据库中的密文。
多次匹配Demo
CredentialsMatcher是一个接口,功能就是用来匹配用户登录使用的令牌和数据库中保存的用户信息是否匹配。当然它的功能不仅如此。本文要介绍的是这个接口的一个实现类:HashedCredentialsMatcher
public class RetryLimitHashedCredentialsMatcher extends HashedCredentialsMatcher {
// 声明一个缓存接口,这个接口是Shiro缓存管理的一部分,它的具体实现可以通过外部容器注入
private Cache<String, AtomicInteger> passwordRetryCache;
public RetryLimitHashedCredentialsMatcher(CacheManager cacheManager) {
passwordRetryCache = cacheManager.getCache("passwordRetryCache");
}
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
String username = (String) token.getPrincipal();
AtomicInteger retryCount = passwordRetryCache.get(username);
if (retryCount == null) {
retryCount = new AtomicInteger(0);
passwordRetryCache.put(username, retryCount);
}
// 自定义一个验证过程:当用户连续输入密码错误5次以上禁止用户登录一段时间
if (retryCount.incrementAndGet() > 5) {
throw new ExcessiveAttemptsException();
}
boolean match = super.doCredentialsMatch(token, info);
if (match) {
passwordRetryCache.remove(username);
}
return match;
}
}
三种授权方法
编程式/if、else
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
注解式
@RequiresRoles("admin")
public void hello() {
//有权限
}
页面标签式
<shiro:hasRole name="admin">
<!— 有权限—>
</shiro:hasRole>