上一篇讲了 shiro的登录认证 现在讲下shiro授权
首先在config中配置了拦截器
/**
* Shiro的过滤器链
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
/**
* 默认的登陆访问url
*/
shiroFilter.setLoginUrl("/login");
/**
* 登陆成功后跳转的url
*/
shiroFilter.setSuccessUrl("/");
/**
* 没有权限跳转的url
*/
shiroFilter.setUnauthorizedUrl("/global/error");
/**
* 覆盖默认的user拦截器(默认拦截器解决不了ajax请求 session超时的问题,若有更好的办法请及时反馈作者)
*/
HashMap<String, Filter> myFilters = new HashMap<>();
myFilters.put("user", new GunsUserFilter());
shiroFilter.setFilters(myFilters);
/**
* 配置shiro拦截器链
*
* anon 不需要认证
* authc 需要认证
* user 验证通过或RememberMe登录的都可以
*
* 当应用开启了rememberMe时,用户下次访问时可以是一个user,但不会是authc,因为authc是需要重新认证的
*
* 顺序从上到下,优先级依次降低
*
* api开头的接口,走rest api鉴权,不走shiro鉴权
*
*/
Map<String, String> hashMap = new LinkedHashMap<>();
for (String nonePermissionRe : NONE_PERMISSION_RES) {
hashMap.put(nonePermissionRe, "anon");
}
hashMap.put("/**", "user");
shiroFilter.setFilterChainDefinitionMap(hashMap);
return shiroFilter;
}
然后每次请求都进行拦截
此时已登陆进去AuthenticationInfo中已保存登录信息
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
UserAuthService shiroFactory = UserAuthServiceServiceImpl.me();
ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();
List<Long> roleList = shiroUser.getRoleList();
Set<String> permissionSet = new HashSet<>();
Set<String> roleNameSet = new HashSet<>();
for (Long roleId : roleList) {
List<String> permissions = shiroFactory.findPermissionsByRoleId(roleId);
if (permissions != null) {
for (String permission : permissions) {
if (ToolUtil.isNotEmpty(permission)) {
permissionSet.add(permission);
}
}
}
String roleName = shiroFactory.findRoleNameByRoleId(roleId);
roleNameSet.add(roleName);
}
//授权执行类
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permissionSet);
info.addRoles(roleNameSet);
return info;
}
然后进入permission切面
@Aspect
@Component
@Order(200)
public class PermissionAop {
@Autowired
private PermissionCheckService check;
@Pointcut(value = "@annotation(cn.stylefeng.guns.core.common.annotion.Permission)")
private void cutPermission() {
}
@Around("cutPermission()")
public Object doPermission(ProceedingJoinPoint point) throws Throwable {
MethodSignature ms = (MethodSignature) point.getSignature();
Method method = ms.getMethod();
Permission permission = method.getAnnotation(Permission.class);
Object[] permissions = permission.value();
if (permissions.length == 0) {
//检查全体角色
boolean result = check.checkAll();
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
}
} else {
//检查指定角色
boolean result = check.check(permissions);
if (result) {
return point.proceed();
} else {
throw new NoPermissionException();
}
}
}
}
之后进入进行检查
/**
* 权限自定义检查
*/
@Service
@Transactional(readOnly = true)
public class PermissionCheckServiceServiceImpl implements PermissionCheckService {
@Override
public boolean check(Object[] permissions) {
ShiroUser user = ShiroKit.getUser();
if (null == user) {
return false;
}
ArrayList<Object> objects = CollectionUtil.newArrayList(permissions);
String join = CollectionUtil.join(objects, ",");
if (ShiroKit.hasAnyRoles(join)) {
return true;
}
return false;
}
@Override
public boolean checkAll() {
HttpServletRequest request = HttpContext.getRequest();
ShiroUser user = ShiroKit.getUser();
if (null == user) {
return false;
}
String requestURI = request.getRequestURI().replaceFirst(ConfigListener.getConf().get("contextPath"), "");
String[] str = requestURI.split("/");
if (str.length > 3) {
requestURI = "/" + str[1] + "/" + str[2];
}
if (ShiroKit.hasPermission(requestURI)) {
return true;
}
return false;
}
}
之后就是jar包中的源码 具体负责权限验证
/**
* 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
*
* @param permission 权限名
* @return 拥有权限:true,否则false
*/
public static boolean hasPermission(String permission) {
return getSubject() != null && permission != null
&& permission.length() > 0
&& getSubject().isPermitted(permission);
}
然后执行 getSubject().isPermitted(permission) 方法
这时可以看到 Subject 的实现类DelegatingSubject中的方法看到还是从securityManager中取判断
public boolean isPermitted(String permission) {
return hasPrincipals() && securityManager.isPermitted(getPrincipals(), permission);
}
然后可看到AuthorizingRealm中的PermissionResolver 从接口interface PermissionResolver中转化为Permission对象
然后将PrincipalCollection principals转化为保存的认证信息AuthorizationInfo 即将此请求信息与用户权限做比较如果存有此用户权限则可进行访问 否则进行拦截
public boolean isPermitted(PrincipalCollection principals, String permission) {
Permission p = getPermissionResolver().resolvePermission(permission);
return isPermitted(principals, p);
}
public boolean isPermitted(PrincipalCollection principals, Permission permission) {
AuthorizationInfo info = getAuthorizationInfo(principals);
return isPermitted(permission, info);
}
protected boolean isPermitted(Permission permission, AuthorizationInfo info) {
Collection<Permission> perms = getPermissions(info);
if (perms != null && !perms.isEmpty()) {
for (Permission perm : perms) {
if (perm.implies(permission)) {
return true;
}
}
}
return false;
}
此时整个运行流程运行完毕