Authorization(授权)机制
Authorization的三元素:权限、角色、用户。
Permissions:权限状态仅仅代表行为。并不指代与用户、角色的关联。
Roles:Implicit roles,隐式构造角色,应用中包含的行为限定于一套角色,不用声明角色名。(缺点:在修改权限时,需要重新修改代码。)
Excplcit roles,显式构造角色,本质上是一个权限集合。
Users:用户,由Relam来进行权限的判定。
Authorization的三种表现形式:
1.代码中直接编写,例如if……else……。
2.通过JDK的annotation进行权限设定与判断。
3.Jsp/Gsp 的taglib来进行权限的设定与判断。
(比较简易的方法是对用户进行初始化时授权)
用户角色的判定:
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
//show the admin button
} else {
//don't show the button? Grey it out?
}
1.hasRole(String roleName) 用户拥有此角色返回TRUE,否则为FALSE;
2.hasRoles(List<String> roleNames) 用户拥有此角色列表中某几个,则执行此角色权限;
3.hasAllRoles(Collection<String> roleNames) 用户拥有列表中所有角色返回TRUE,否则为FALSE。
用户角色的检测(Assertions,断言判定):
Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is a bank teller and
//therefore allowed to open the account:
currentUser.checkRole("bankTeller");
openBankAccount();
1.checkRole(String roleName) 用户拥有此角色继续执行,否则抛出AuthorizationException异常;
2.checkRoles(Collection<String> roleNames) 用户拥有列表中所有角色继续执行,否则抛出AuthorizationException异常;
3.checkRoles(String... roleNames) 和checkRoles方法类似,可以用var-args格式参数。
权限判定:
1.实例化org.apache.shiro.authz.Permission接口,
Permission printPermission = new PrinterPermission("laserjet4400n", "print");
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted(printPermission)) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
isPermitted(Permission p) 用户有此权限返回TRUE,否则为FALSE;
isPermitted(List<Permission> perms) 用户拥有列表中某些权限,则执行此权限;
isPermittedAll(Collection<Permission> perms) 用户拥有所有权限返回TRUE,否则为FALSE。
2.用一个String来限定权限
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isPermitted("printer:print:laserjet4400n")) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
3.org.apache.shiro.authz.permission.WildcardPermission的实现(可以自定义自己的权限标识模型)
Subject currentUser = SecurityUtils.getSubject();
Permission p = new WildcardPermission("printer:print:laserjet4400n");
if (currentUser.isPermitted(p) {
//show the Print button
} else {
//don't show the button? Grey it out?
}
isPermitted(String perm) 用户拥有此权限返回TRUE,否则返回FALSE;
isPermitted(String... perms) 用户拥有所有权限中几个,执行此权限;
isPermittedAll(String... perms) 用户拥有所有权限返回TRUE,否则为FALSE。
权限检测(权限的断言判定):
Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
Permission p = new AccountPermission("open");
currentUser.checkPermission(p);
openBankAccount();
或者,
Subject currentUser = SecurityUtils.getSubject();
//guarantee that the current user is permitted
//to open a bank account:
currentUser.checkPermission("account:open");
openBankAccount();
1.checkPermission(Permission p) 用户拥有此权限则继续执行,否则抛出AuthorizationException异常;
2.checkPermission(String perm) 与上面方法相同,只是参数类型不同;
3.checkPermissions(Collection<Permission> perms) 用户拥有所有权限则继续执行,否则抛出AuthorizationException异常;
4.checkPermissions(String... perms) 与上面方法相似,只是参数不同。
Authorization基于Annotation
案例一(认证角色验证):
@RequiresAuthentication
public void updateAccount(Account userAccount) {
//this method will only be invoked by a
//Subject that is guaranteed authenticated
...
}
等同于
public void updateAccount(Account userAccount) {
if (!SecurityUtils.getSubject().isAuthenticated()) {
throw new AuthorizationException(...);
}
//Subject is guaranteed authenticated here
...
}
案例二(游客角色验证):
@RequiresGuest
public void signUp(User newUser) {
//this method will only be invoked by a
//Subject that is unknown/anonymous
...
}
等同于
public void signUp(User newUser) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals != null && !principals.isEmpty()) {
//known identity - not a guest:
throw new AuthorizationException(...);
}
//Subject is guaranteed to be a 'guest' here
...
}
案例三(账户创建权限验证):
@RequiresPermissions("account:create")
public void createAccount(Account account) {
//this method will only be invoked by a Subject
//that is permitted to create an account
...
}
等同于
public void createAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.isPermitted("account:create")) {
throw new AuthorizationException(...);
}
//Subject is guaranteed to be permitted here
...
}
案例四(超级管理员角色验证):
@RequiresRoles("administrator")
public void deleteUser(User user) {
//this method will only be invoked by an administrator
...
}
等同于
public void deleteUser(User user) {
Subject currentUser = SecurityUtils.getSubject();
if (!subject.hasRole("administrator")) {
throw new AuthorizationException(...);
}
//Subject is guaranteed to be an 'administrator' here
...
}
案例五(记住用户验证):
@RequiresUser
public void updateAccount(Account account) {
//this method will only be invoked by a 'user'
//i.e. a Subject with a known identity
...
}
等同于
public void updateAccount(Account account) {
Subject currentUser = SecurityUtils.getSubject();
PrincipalCollection principals = currentUser.getPrincipals();
if (principals == null || principals.isEmpty()) {
//no identity - they're anonymous, not allowed:
throw new AuthorizationException(...);
}
//Subject is guaranteed to have a known identity here
...
}
Authorization的内部实现
1.subject的验证检测方法接收参数;
2.交由securityManager内部实现的 org.apache.shiro.authz.Authorizer接口的检验验证方法进行验证;
3.Authorizer调用默认的 ModularRealmAuthorizer进行验证检测;
4.配置中的Relam都检查自己是否实现自Authorizer接口,如果是则启动进行验证检测。
(如果配置中Relam匹配Authorizer,一方面,如果Relam处理抛出异常,则继续其他授权及验证;另一方面,如果Relam处理返回TRUE,则验证通过。如果Relam不匹配Authorizer,则不处理。)
设置全局PermissionResolver(转换String权限标识为Permission对象),可以进行自定义设置(shiro.ini)
globalPermissionResolver = com.foo.bar.authz.MyPermissionResolver
...
securityManager.authorizer.permissionResolver = $globalPermissionResolver
...
每个接收配置中PermissionResolver的Relam都需要实现PermisionResolverAware接口。
为了避免多次实现PermissionResolverAware接口,可以直接配置一个特定的Relam:
permissionResolver = com.foo.bar.authz.MyPermissionResolver
realm = com.foo.bar.realm.MyCustomRealm
realm.permissionResolver = $permissionResolver
...
设置全局RolePermissionResolver(转换String的角色名为一个实体对象),可以自定义配置(shiro.ini)
globalRolePermissionResolver = com.foo.bar.authz.MyPermissionResolver
...
securityManager.authorizer.rolePermissionResolver = $globalRolePermissionResolver
...
每个接收配置中RolePermissionResolver的Relam都需要实现RolePermissionResolverAware接口。
为了避免多次实现RolePermissionResolverAware接口,可以直接配置一个特定的Relam:
rolePermissionResolver = com.foo.bar.authz.MyRolePermissionResolver
realm = com.foo.bar.realm.MyCustomRealm
realm.rolePermissionResolver = $rolePermissionResolver
...
如果应用中授权的Relam多于一个,则默认的ModularRealmAuthorizer的简单型短路迭代机制就不实用了,需要自己定义更为强大的Authorizer(shiro.ini)
[main]
...
authorizer = com.foo.bar.authz.CustomAuthorizer
securityManager.authorizer = $authorizer