权限认证流程
对 isPermitted 进行分析
public static void main(String[] args) {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken();
usernamePasswordToken.setUsername("root");
usernamePasswordToken.setPassword("secret".toCharArray());
subject.login(usernamePasswordToken);
System.out.println("访问权限认证:"+subject.isPermitted("user:myview"));
}
subject.isPermitted(“user:myview”)
DelegatingSubject
public boolean isPermitted(String permission) {
return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
}
级别2
hasPrincipals()
权限列表是不是为空
DelegatingSubject
public PrincipalCollection getPrincipals() {
return CollectionUtils.isEmpty(this.runAsPrincipals) ? this.principals : this.runAsPrincipals.get(0);
}
protected boolean hasPrincipals() {
return !CollectionUtils.isEmpty(getPrincipals());
}
级别3
securityManager.isPermitted(getPrincipals(), permission);
委托给 securityManager 进行权限判断
AuthorizingSecurityManager
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
return this.authorizer.isPermitted(principals, permissionString);
}
级别4
securityManager.isPermitted(getPrincipals(), permission);
委托给 authorizer 进行权限判断
AuthorizingSecurityManager
public boolean isPermitted(PrincipalCollection principals, String permissionString) {
return this.authorizer.isPermitted(principals, permissionString);
}
级别5
this.authorizer.isPermitted(principals, permissionString);
用所有的realm进行认证,第一个realm认证成功就返回
ModularRealmAuthorizer
public boolean isPermitted(PrincipalCollection principals, String permission) {
// realm 为空就抛出异常
assertRealmsConfigured();
for (Realm realm : getRealms()) {
if (realm.isPermitted(principals, permission)) {
return true;
}
}
return false;
}
级别6
realm.isPermitted(principals, permission)
IniRealm 权限认证(IniRealm的间接父类AuthorizingRealm)
AuthorizingRealm
public boolean isPermitted(PrincipalCollection principals, String permission) {
Permission p = getPermissionResolver().resolvePermission(permission);
return isPermitted(principals, p);
}
级别7-1
Permission p = getPermissionResolver().resolvePermission(permission);
AuthorizingRealm 构造器中初始化了permissionResolver,
返回通配符权限对象
AuthorizingRealm
public AuthorizingRealm() {
this.authorizationCachingEnabled = true;
this.permissionResolver = new WildcardPermissionResolver();
int instanceNumber = INSTANCE_COUNT.getAndIncrement();
this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
if (instanceNumber > 0) {
this.authorizationCacheName = this.authorizationCacheName + "." + instanceNumber;
}
}
public PermissionResolver getPermissionResolver() {
return permissionResolver;
}
PermissionResolver
public Permission resolvePermission(String permissionString) {
// 参见 [通配符权限初始化]
return new WildcardPermission(permissionString);
}
级别7-2
return isPermitted(principals, p);
- 通过凭证(PrincipalCollection) 获取认证信息
- 通过认证信息(AuthorizationInfo) 获取权限列表 (Collection)
- 遍历权限列表,当权限能匹配目标权限时,返回true. 全部匹配失败就返回false
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
// 参见 [realm 通过凭证获取认证信息]
AuthorizationInfo info = getAuthorizationInfo(principals);
return isPermitted(permission, info);
}
private boolean isPermitted(Permission permission, AuthorizationInfo info) {
# 参见 [Realm 通过认证信息获取权限集合]
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
# 参见 [Permission 权限匹配]
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
附录
通配符权限初始化
通配符权限解析器 WildcardPermissionResolver 实现了 PermissionResolver
级别1 创建通配符权限对象
public class WildcardPermissionResolver implements PermissionResolver {
/**
* Returns a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified
* <tt>permissionString</tt>.
*
* @param permissionString the permission string to convert to a {@link Permission Permission} instance.
* @return a new {@link WildcardPermission WildcardPermission} instance constructed based on the specified
* <tt>permissionString</tt>
*/
public Permission resolvePermission(String permissionString) {
return new WildcardPermission(permissionString);
}
}
级别2 创建通配符权限对象
return new WildcardPermission(permissionString);
将通配符对象的this.parts 按照冒号分割,在进行填充
WildcardPermission
protected static final boolean DEFAULT_CASE_SENSITIVE = false;
// 默认大小写敏感: DEFAULT_CASE_SENSITIVE=false
public WildcardPermission(String wildcardString) {
this(wildcardString, DEFAULT_CASE_SENSITIVE);
}
public WildcardPermission(String wildcardString, boolean caseSensitive) {
setParts(wildcardString, caseSensitive);
}
protected static final String PART_DIVIDER_TOKEN = ":";
protected static final String SUBPART_DIVIDER_TOKEN = ",";
protected void setParts(String wildcardString, boolean caseSensitive) {
if (wildcardString == null || wildcardString.trim().length() == 0) {
throw new IllegalArgumentException("Wildcard string cannot be null or empty. Make sure permission strings are properly formatted.");
}
wildcardString = wildcardString.trim();
// PART_DIVIDER_TOKEN 是权限层级分割符 ':' , 例如 winnebago:drive:eagle5 将被分割为 [weinebago,drive,eagle5]
List<String> parts = CollectionUtils.asList(wildcardString.split(PART_DIVIDER_TOKEN));
this.parts = new ArrayList<Set<String>>();
for (String part : parts) {
// 将每个权限级别用 ',' 分割, 事实上没有用, 数组长度一定为1, 参见[权限解析之逗号分割重复]
Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
// 大小写不敏感就转换为小写
if (!caseSensitive) {
subparts = lowercase(subparts);
}
if (subparts.isEmpty()) {
throw new IllegalArgumentException("Wildcard string cannot contain parts with only dividers. Make sure permission strings are properly formatted.");
}
this.parts.add(subparts);
}
if (this.parts.isEmpty()) {
throw new IllegalArgumentException("Wildcard string cannot contain only dividers. Make sure permission strings are properly formatted.");
}
}
权限解析之逗号分割重复
调用堆栈
类 | 方法 | 调用方法 |
---|---|---|
TextConfigurationRealm | processRoleDefinitions(Map |
public static Set<Permission> resolveDelimitedPermissions(String s, PermissionResolver permissionResolver) {
Set<String> permStrings = toPermissionStrings(s);
return resolvePermissions(permStrings, permissionResolver);
}
// 通过逗号分割权限, 例如 schwartz = lightsaber:*,order:* 将被分割为 ["lightsaber:*","order:*"]
public static Set<String> toPermissionStrings(String permissionsString) {
String[] tokens = StringUtils.split(permissionsString);
if (tokens != null && tokens.length > 0) {
return new LinkedHashSet<String>(Arrays.asList(tokens));
}
return null;
}
级别2
在 WildcardPermission的setParts 又将 lightsaber:* 分割一次, order:* 分割一次。
事实上已经无法再次分割了
WildcardPermission
protected void setParts(String wildcardString, boolean caseSensitive) {
//TODO 省略
for (String part : parts) {
Set<String> subparts = CollectionUtils.asSet(part.split(SUBPART_DIVIDER_TOKEN));
}
Realm 通过凭证获取认证信息
AuthorizationInfo info = getAuthorizationInfo(principals);
- 判断凭证信息是否为空,为空就返回空
- 从缓存中获取认证信息,获取到就返回
- 调用模板方法 doGetAuthorizationInfo,获取认证信息. 如果缓存不为空,并且认证信息也不为空 就将认证信息和凭证加入到缓存
AuthorizingRealm
protected AuthorizationInfo getAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
return null;
}
AuthorizationInfo info = null;
if (log.isTraceEnabled()) {
log.trace("Retrieving AuthorizationInfo for principals [" + principals + "]");
}
Cache<Object, AuthorizationInfo> cache = getAvailableAuthorizationCache();
if (cache != null) {
if (log.isTraceEnabled()) {
log.trace("Attempting to retrieve the AuthorizationInfo from cache.");
}
Object key = getAuthorizationCacheKey(principals);
info = cache.get(key);
if (log.isTraceEnabled()) {
if (info == null) {
log.trace("No AuthorizationInfo found in cache for principals [" + principals + "]");
} else {
log.trace("AuthorizationInfo found in cache for principals [" + principals + "]");
}
}
}
if (info == null) {
// Call template method if the info was not found in a cache
info = doGetAuthorizationInfo(principals);
// If the info is not null and the cache has been created, then cache the authorization info.
if (info != null && cache != null) {
if (log.isTraceEnabled()) {
log.trace("Caching authorization info for principals: [" + principals + "].");
}
Object key = getAuthorizationCacheKey(principals);
cache.put(key, info);
}
}
return info;
}
级别2-1
Cache
public Cache<Object, AuthorizationInfo> getAuthorizationCache() {
return this.authorizationCache;
}
public boolean isCachingEnabled() {
return cachingEnabled;
}
public boolean isAuthorizationCachingEnabled() {
return isCachingEnabled() && authorizationCachingEnabled;
}
private Cache<Object, AuthorizationInfo> getAvailableAuthorizationCache() {
Cache<Object, AuthorizationInfo> cache = getAuthorizationCache();
if (cache == null && isAuthorizationCachingEnabled()) {
cache = getAuthorizationCacheLazy();
}
return cache;
}
级别3
cache = getAuthorizationCacheLazy();
- 获取缓存管理器
getCacheManager在父类CachingRealm 中定义.
CacheManager里面只定义了一个方法:
public
// 延迟加载缓存信息
private Cache<Object, AuthorizationInfo> getAuthorizationCacheLazy() {
if (this.authorizationCache == null) {
if (log.isDebugEnabled()) {
log.debug("No authorizationCache instance set. Checking for a cacheManager...");
}
CacheManager cacheManager = getCacheManager();
if (cacheManager != null) {
// 在AuthorizingRealm 的构造方法中初始化为: this.authorizationCacheName = getClass().getName() + DEFAULT_AUTHORIZATION_CACHE_SUFFIX;
// 在本例中的名称为: iniRealm.authorizationCache
String cacheName = getAuthorizationCacheName();
if (log.isDebugEnabled()) {
log.debug("CacheManager [" + cacheManager + "] has been configured. Building " +
"authorization cache named [" + cacheName + "]");
}
this.authorizationCache = cacheManager.getCache(cacheName);
} else {
if (log.isInfoEnabled()) {
log.info("No cache or cacheManager properties have been set. Authorization cache cannot " +
"be obtained.");
}
}
}
return this.authorizationCache;
}
级别2-2
info = doGetAuthorizationInfo(principals);
通过模板方法获取认证信息
- 获得 principals 的用户名
- 通过ini文件加载的用户和认证信息的 Map
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return this.users.get(getUsername(principals));
}
// 获取有效的权限,调用父类的getAvailablePrincipal
protected String getUsername(PrincipalCollection principals) {
return getAvailablePrincipal(principals).toString();
}
级别3
return getAvailablePrincipal(principals).toString();
- 从realm的名称中获得当前的名称
- 获取到了就直接返回
- 没有获取到就获取 凭证信息principals 的realmPrincipals的values。 (返回的是一个不可修改的LinkedHashSet)
AuthorizingRealm
protected Object getAvailablePrincipal(PrincipalCollection principals) {
if (principals == null || principals.isEmpty()) {
return null;
}
Object primary;
// 从realm的名称中获得当前的名称
Collection thisPrincipals = principals.fromRealm(getName());
if (thisPrincipals != null && !thisPrincipals.isEmpty()) {
primary = thisPrincipals.iterator().next();
} else {
//no principals attributed to this particular realm. Fall back to the 'master' primary:
primary = principals.getPrimaryPrincipal();
}
return primary;
}
SimplePrincipalCollection
public Collection fromRealm(String realmName) {
if (realmPrincipals == null || realmPrincipals.isEmpty()) {
return Collections.EMPTY_SET;
}
Set principals = realmPrincipals.get(realmName);
if (principals == null || principals.isEmpty()) {
principals = Collections.EMPTY_SET;
}
return Collections.unmodifiableSet(principals);
}
Realm 通过认证信息获取权限集合
- 新建 Set permissions = new HashSet();
- 获取info的 authzInfo.getObjectPermissions(); 添加到 permissions
- 获取info的 authzInfo.getStringPermissions(); 在将获取的Collection 解析为 Collection 添加到 permissions
- 获取info的 authzInfo.getRoles(); 在将获取的Collection 解析为 Collection 添加到 permissions
- 为空就返回 emptySet, 不为空就返回不可修改的Set ( Collections.unmodifiableSet(permissions); )
private Collection<Permission> getPermissions(AuthorizationInfo info) {
Set<Permission> permissions = new HashSet<Permission>();
if (info != null) {
Collection<Permission> perms = info.getObjectPermissions();
if (!CollectionUtils.isEmpty(perms)) {
permissions.addAll(perms);
}
perms = resolvePermissions(info.getStringPermissions());
if (!CollectionUtils.isEmpty(perms)) {
permissions.addAll(perms);
}
perms = resolveRolePermissions(info.getRoles());
if (!CollectionUtils.isEmpty(perms)) {
permissions.addAll(perms);
}
}
if (permissions.isEmpty()) {
return Collections.emptySet();
} else {
return Collections.unmodifiableSet(permissions);
}
}
Permission 权限匹配
if (perm.implies(permission)) {
- 判断 目标是不是 WildcardPermission ,不是就返回匹配失败
- 获取当前的parts进行遍历判断
- 如果当前的part不存在通配符,并且当前的part不是完全包含目标的part 就返回false
- 再次进行parts遍历判断,如果part 不能再通配符就返回false
匹配的三种形式
1. 完全匹配
2. 小于目标部件(thisPart 小于 targetPart)
3. 大于目标部件(thisPart 大于 targetPart)
WildcardPermission
public boolean implies(Permission p) {
// By default only supports comparisons with other WildcardPermissions
if (!(p instanceof WildcardPermission)) {
return false;
}
WildcardPermission wp = (WildcardPermission) p;
List<Set<String>> otherParts = wp.getParts();
int i = 0;
for (Set<String> otherPart : otherParts) {
// If this permission has less parts than the other permission, everything after the number of parts contained
// in this permission is automatically implied, so return true
// 如果当前权限比目标权限更少,那就说明必然包含后面的内容
// 简而言之: 当前parts: lightsaber:* 必然包含目标part: lightsaber:a:b:c 就直接返回true
// 路线2 : 小于目标部件
if (getParts().size() - 1 < i) {
return true;
} else {
// order:a:*:b 包含 order:a:c:b
Set<String> part = getParts().get(i);
if (!part.contains(WILDCARD_TOKEN) && !part.containsAll(otherPart)) {
return false;
}
i++;
}
}
// 如果当前权限多余目标权限,那么当前权限必须是 通配符 *
// 看例子
// order:a:b 不匹配 order:a
// order:a:* 匹配 order:a
// order:a:*:*:* 匹配 order:a, order:a:b
// 虽然没有人写 order:a:*:*:* 的形式,但是从逻辑上来说这样写也可以
// If this permission has more parts than the other parts, only imply it if all of the other parts are wildcards
// 路线3 : 大于目标部件
for (; i < getParts().size(); i++) {
Set<String> part = getParts().get(i);
if (!part.contains(WILDCARD_TOKEN)) {
return false;
}
}
// 路线1 : 完全匹配
return true;
}
匹配示例:
类型 | 当前匹配符 | 目标路径 | 匹配结果 |
完全匹配 | a:b:c | a:b:c | 匹配 |
完全匹配 | a:b:* | a:b:c | 匹配 |
完全匹配 | x:y | x:a | 不匹配 |
小于目标部件 | x:y:* | x:y:z:d | 匹配 |
小于目标部件 | x:y | a:b:d:e | 不匹配 |
小于目标部件 | a:* | a:b:c:d:e | 不匹配 |
大于目标部件 | order:a:b:c | order:a | 不匹配 |
大于目标部件 | order:a:* | order:a | 匹配 |
大于目标部件 | order:a:*:*:* | order:a | 匹配 |
默认过滤器
- 来源: https://blog.csdn.net/cheng43526899/article/details/53837635
简称 类 例子 anon org.apache.shiro.web.filter.authc.AnonymousFilter /admins/**=anon 没有参数,表示可以匿名使用。 authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter /admins/user/**=authc表示需要认证(登录)才能使用,没有参数 authcBasic org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter /admins/user/**=authcBasic没有参数表示httpBasic认证 perms org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter /admins/user/=perms[user:add:],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/=perms[“user:add:,user:modify:*”],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 port org.apache.shiro.web.filter.authz.PortFilter /admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。 rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter /admins/user/=rest[user],根据请求的方法,相当于/admins/user/=perms[user:method] ,其中method为post,get,delete等。 roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter /admins/user/=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/=roles[“admin,guest”],每个参数通过才算通过,相当于hasAllRoles()方法。 ssl org.apache.shiro.web.filter.authz.SslFilter /admins/user/**=ssl没有参数,表示安全的url请求,协议为https user org.apache.shiro.web.filter.authc.UserFilter /admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查 logout org.apache.shiro.web.filter.authc.LogoutFilter