官方文档:https://www.infoq.com/articles/apache-shiro/
参考:https://www.jianshu.com/p/7f724bec3dc3
为什么使用 Shiro
- 易于使用——易用性是项目的最终目标。
- 全面——没有其他安全框架的宽度范围可以同Apache Shiro一样,它可以成为你的“一站式”为您的安全需求提供保障。
- 灵活——可以在任何应用程序环境中工作。虽然在网络工作、EJB和IoC环境中可能并不需要它。但Shiro的授权也没有任何规范,甚至没有许多依赖关系。
- Web支持——允许您基于应用程序的url创建灵活的安全策略和网络协议(例如REST)。
- 低耦合——Shiro干净的API和设计模式使它容易与许多其他框架和应用程序集成。
- 被广泛支持——Apache Shiro是Apache软件基金会的一部分。
High-Level Overview 高级概述
在概念层,Shiro 架构包含三个主要的理念:Subject,SecurityManager和 Realm。
- Subject:当前用户,Subject 可以是一个人,但也可以是第三方服务、守护进程帐户、时钟守护任务或者其它–当前和软件交互的任何事件。
- SecurityManager:管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞。
- Realms:用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和授权(authorization)。
我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。
细分功能
Authentication:身份认证/登录(账号密码验证)。
Authorization:授权,即角色或者权限验证。
Session Manager:会话管理,用户登录后的session相关管理。
Cryptography:加密,密码加密等。
Web Support:Web支持,集成Web环境。
Caching:缓存,用户信息、角色、权限等缓存到如redis等缓存中。
Concurrency:多线程并发验证,在一个线程中开启另一个线程,可以把权限自动传播过去。
Testing:测试支持。
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
Remember Me:记住我,登录后,下次再来就不用登录了。
Shiro 认证过程
流程如下:
- 首先调用
Subject.login(token)
进行登录,其会自动委托给 Security Manager,调用之前必须通过SecurityUtils.setSecurityManager()
设置; - SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
Shiro 授权过程
跟认证过程大致相似
Shiro 加密(未实现)
一件构建系统采用YYOA和LDAP双认证登录,不需要Shiro框架支持
在之前的学习中,我们在数据库中保存的密码都是明文的,一旦数据库数据泄露,那就会造成不可估算的损失,所以我们通常都会使用非对称加密,简单理解也就是不可逆的加密,而 md5 加密算法就是符合这样的一种算法。
如 123456 用 Md5 加密后,得到的字符串:e10adc3949ba59abbe56e057f20f883e,就无法通过计算还原回 123456,我们把这个加密的字符串保存在数据库中,等下次用户登录时我们把密码通过同样的算法加密后再从数据库中取出这个字符串进行比较,就能够知道密码是否正确了,这样既保留了密码验证的功能又大大增加了安全性,但是问题是:虽然无法直接通过计算反推回密码,但是我们仍然可以通过计算一些简单的密码加密后的 Md5 值进行比较,推算出原来的密码
加盐 + 多次加密
既然相同的密码 md5 一样,那么我们就让我们的原始密码再加一个随机数,然后再进行 md5 加密,这个随机数就是我们说的盐(salt),这样处理下来就能得到不同的 Md5 值,当然我们需要把这个随机数盐也保存进数据库中,以便我们进行验证。
另外我们可以通过多次加密的方法,即使黑客通过一定的技术手段拿到了我们的密码 md5 值,但它并不知道我们到底加密了多少次,所以这也使得破解工作变得艰难。
在 Shiro 框架中,对于这样的操作提供了简单的代码实现:
String password = "123456";
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
int times = 2; // 加密次数:2
String alogrithmName = "md5"; // 加密算法
String encodePassword = new SimpleHash(alogrithmName, password, salt, times).toString();
System.out.printf("原始密码是 %s , 盐是: %s, 运算次数是: %d, 运算出来的密文是:%s ",password,salt,times,encodePassword);
//output
原始密码是 123456 , 盐是: f5GQZsuWjnL9z585JjLrbQ==, 运算次数是: 2, 运算出来的密文是:55fee80f73537cefd6b3c9a920993c25
快速开始
加pom
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.6.0</version>
</dependency>
增加entity
User.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
@Data
@EqualsAndHashCode(callSuper=false)
@Builder
@AllArgsConstructor
@TableName(autoResultMap = true)
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private String userName;
/**
* 用户对应的角色集合
*/
@TableField(exist = false)
private Set<Role> roles;
public User() {
this.roles = new HashSet<>();
}
}
因为我们项目已经有YYOA认证登录了,user表这边就不存密码了,并且采用无验证登录方式。
UserRole.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
@Data
@EqualsAndHashCode(callSuper=false)
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(autoResultMap = true)
public class UserRole {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private int userId;
private int roleId;
}
Role.java
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
/**
* @author wujinquan
* @since 2021-06-04
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
@AllArgsConstructor
@TableName(autoResultMap = true)
public class Role implements Serializable {
private Integer id;
/**
* 角色编码
*/
private String roleCode;
/**
* 角色名
*/
private String roleName;
/**
* 角色对应权限集合
*/
@TableField(exist = false)
private Set<Permission> permissions;
public Role(){
this.permissions = new HashSet<>();
}
}
RolePermission.java
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.io.Serializable;
/**
* @author wujinquan
* @since 2021-06-04
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(autoResultMap = true)
public class RolePermission implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
private int roleId;
private int permissionId;
}
Permission.java
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* @author wujinquan
* @since 2021-06-04
*/
@Data
@EqualsAndHashCode(callSuper = false)
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(autoResultMap = true)
public class Permission {
private Integer id;
/**
* 权限名
*/
private String permissionName;
/**
* 权限编码
*/
private String permissionCode;
/**
* 备注
*/
private String permissionNote;
}
数据库建表
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`
(
`id` int NOT NULL AUTO_INCREMENT,
`user_name` char(64) NOT NULL COMMENT '用户名称',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='用户表';
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`
(
`id` int NOT NULL AUTO_INCREMENT,
`role_code` char(64) NOT NULL COMMENT '角色编码',
`role_name` char(64) NOT NULL COMMENT '角色名',
PRIMARY KEY (`id`),
UNIQUE (`role_code`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='角色表';
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role`
(
`id` int NOT NULL AUTO_INCREMENT,
`user_id` int NOT NULL COMMENT '用户id',
`role_id` int NOT NULL COMMENT '角色id',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='用户角色表';
DROP TABLE IF EXISTS `role_permission`;
CREATE TABLE `role_permission`
(
`id` int NOT NULL AUTO_INCREMENT,
`role_id` int NOT NULL COMMENT '角色id',
`permission_id` int NOT NULL COMMENT '权限id',
PRIMARY KEY (`id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='角色权限表';
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`
(
`id` int NOT NULL AUTO_INCREMENT,
`permission_name` char(64) NOT NULL COMMENT '权限名',
`permission_code` char(64) NOT NULL COMMENT '权限码',
`permission_note` char(128) NOT NULL COMMENT '权限描述' default "",
PRIMARY KEY (`id`),
UNIQUE (`permission_name`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='权限表';
AuthorizationServiceImpl.java
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yy.testdev.autobuild.entity.*;
import com.yy.testdev.autobuild.mapper.*;
import com.yy.testdev.autobuild.service.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
/**
* 鉴权
*
* @author wujinquan
* @since 2021-06-07
*/
@Slf4j
@Service
public class AuthorizationServiceImpl implements AuthorizationService {
@Resource
UserMapper userMapper;
@Resource
UserRoleMapper userRoleMapper;
@Resource
RoleMapper roleMapper;
@Resource
RolePermissionMapper rolePermissionMapper;
@Resource
PermissionMapper permissionMapper;
@Override
public User getUserByName(String getMapByName) {
return getMapByName(getMapByName);
}
/**
*
* @param userName 用户名
* @return User
*/
private User getMapByName(String userName) {
User user = userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUserName, userName));
List<UserRole> userRoles = userRoleMapper.selectList(new LambdaQueryWrapper<UserRole>().eq(UserRole::getUserId, user.getId()));
Set<Integer> temp = new HashSet<>();
for (UserRole userRole : userRoles) {
temp.add(userRole.getRoleId());
}
if (temp.size() == 0){
return user;
}
List<Role> roles = roleMapper.selectList(new LambdaQueryWrapper<Role>().in(Role::getId, temp));
for (Role role : roles) {
List<RolePermission> rolePermissions = rolePermissionMapper.selectList(new LambdaQueryWrapper<RolePermission>().eq(RolePermission::getRoleId,role.getId()));
temp.clear();
for (RolePermission rolePermission : rolePermissions) {
temp.add(rolePermission.getPermissionId());
}
if (temp.size() == 0){
continue;
}
List<Permission> permissions = permissionMapper.selectList(new LambdaQueryWrapper<Permission>().in(Permission::getId, temp));
HashSet<Permission> permissionHashSet = new HashSet<>(permissions);
role.setPermissions(permissionHashSet);
}
HashSet<Role> roleSet = new HashSet<>(roles);
user.setRoles(roleSet);
return user;
}
}
CustomRealm.java
自定义Realm用于查询用户的角色和权限信息并保存到权限管理器
import cn.hutool.core.util.StrUtil;
import com.yy.testdev.autobuild.entity.Permission;
import com.yy.testdev.autobuild.entity.Role;
import com.yy.testdev.autobuild.entity.User;
import com.yy.testdev.autobuild.service.AuthorizationService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
/**
* Shiro框架自定义Realm,用于认证和授权配置
*
* @author wujinquan
* @since 2021-06-07
*/
@Slf4j
public class CustomRealm extends AuthorizingRealm {
private AuthorizationService authorizationService;
public CustomRealm(AuthorizationService authorizationService) {
this.authorizationService = authorizationService;
}
/**
* Shiro授权
*
* @param principalCollection 角色集合
* @return 授权信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String name = (String) principalCollection.getPrimaryPrincipal();
//查询用户
User user = authorizationService.getUserByName(name);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleCode());
//添加权限
for (Permission permission : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permission.getPermissionCode());
}
}
return simpleAuthorizationInfo;
}
/**
* Shiro认证
*
* @param authenticationToken token
* @return 认证信息
* @throws AuthenticationException 认证异常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//设置为无验证登录
setCredentialsMatcher(new AllowAllCredentialsMatcher());
if (StrUtil.isEmptyIfStr(authenticationToken.getPrincipal())) {
return null;
}
//获取用户名
String name = authenticationToken.getPrincipal().toString();
User user = authorizationService.getUserByName(name);
if (user == null) {
//这里返回后会报出对应异常
return null;
} else {
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, "", getName());
return simpleAuthenticationInfo;
}
}
}
ShiroConfig.java
把CustomRealm和SecurityManager等注入到spring容器中:
import com.yy.testdev.autobuild.util.shiro.CustomRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* Shiro配置
*
* @author wujinquan
* @since 2021-06-08
*/
@Configuration
public class ShiroConfig {
/**
* Filter工厂,设置对应的过滤条件和跳转条件
*
* @param securityManager 安全管理器
* @return 过滤器Bean
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
//配置map.put("/**", "anon")可以匿名访问任意接口,本地开发用
// map.put("/**", "anon");
//对所有用户认证
// map.put("/**", "authc");
// //登出
// map.put("/logout", "logout");
// //登录
// shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* ConditionalOnMissingBean注解保证bean只有一个
*
* @return 声明式Aop DefaultAdvisorAutoProxyCreator
*/
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultApp = new DefaultAdvisorAutoProxyCreator();
defaultApp.setProxyTargetClass(true);
return defaultApp;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
/**
* 安全管理,配置主要是Realm的管理认证
*
* @return 安全管理器
*/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
/**
* 匹配所有类,匹配所有加了认证注解的方法
* RequiresPermissions, RequiresRoles, RequiresUser, RequiresGuest, RequiresAuthentication注解work
*
* @param securityManager 安全管理器
* @return pointCut切点
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
LoginController.java
编写一个简单的登录方法
import com.wsl.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@Slf4j
public class LoginController {
@GetMapping("/login")
public String login(User user) {
//用户认证信息
Subject subject = SecurityUtils.getSubject();
// UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(),user.getPassword());
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUserName(),"");
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
// subject.checkRole("admin");
// subject.checkPermissions("query", "add");
} catch (UnknownAccountException e) {
log.error("用户名不存在!", e);
return "用户名不存在!";
} catch (AuthenticationException e) {
log.error("账号或密码错误!", e);
return "账号或密码错误!";
} catch (AuthorizationException e) {
log.error("没有权限!", e);
return "没有权限";
}
return "login success";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("query")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("add")
@GetMapping("/add")
public String add() {
return "add success!";
}
}
AuthorizationAttributeSourceAdvisor配置的作用
观察类层次结构图,可以看到它实现了pointcut
,我们知道pointcut就是切点,它会判断匹配哪些类,并返回方法匹配
public interface Pointcut {
/**
* Return the ClassFilter for this pointcut.
* @return the ClassFilter (never {@code null})
*/
ClassFilter getClassFilter();
/**
* Return the MethodMatcher for this pointcut.
* @return the MethodMatcher (never {@code null})
*/
MethodMatcher getMethodMatcher();
/**
* Canonical Pointcut instance that always matches.
*/
Pointcut TRUE = TruePointcut.INSTANCE;
}
pointcut中一共两个接口方法,一个是getClassFilter
,一个是getMethodMatcher
。
getClassFilter匹配所有类
AuthorizationAttributeSourceAdvisor
的父类StaticMethodMatcherPointcut
中,实现了getClassFilter
、getMethodMatcher
方法。
public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {
private ClassFilter classFilter = ClassFilter.TRUE;
/**
* Set the {@link ClassFilter} to use for this pointcut.
* Default is {@link ClassFilter#TRUE}.
*/
public void setClassFilter(ClassFilter classFilter) {
this.classFilter = classFilter;
}
@Override
public ClassFilter getClassFilter() {
return this.classFilter;
}
@Override
public final MethodMatcher getMethodMatcher() {
return this;
}
}
继续追踪,类属性classFilter是ClassFilter.TRUE
,最终跟踪到TrueClassFilter
,matches方法始终返回true,所以AuthorizationAttributeSourceAdvisor
会匹配所有类!
class TrueClassFilter implements ClassFilter, Serializable {
public static final TrueClassFilter INSTANCE = new TrueClassFilter();
/**
* Enforce Singleton pattern.
*/
private TrueClassFilter() {
}
@Override
public boolean matches(Class<?> clazz) {
return true;
}
}
getMethodMatcher匹配所有加了认证注解的方法
再看方法匹配,注意StaticMethodMatcherPointcut的getMethodMatcher返回的是this,因为AuthorizationAttributeSourceAdvisor实现了MethodMatcher接口,所以返回的this就是AuthorizationAttributeSourceAdvisor本身。MethodMatchermatches方法用来判断方法匹配。
可以看到它会匹配所有加了认证注解的方法。
public class AuthorizationAttributeSourceAdvisor extends StaticMethodMatcherPointcutAdvisor {
private static final Class<? extends Annotation>[] AUTHZ_ANNOTATION_CLASSES = new Class[]{
RequiresPermissions.class, RequiresRoles.class, RequiresUser.class, RequiresGuest.class, RequiresAuthentication.class
};
public boolean matches(Method method, Class targetClass) {
Method m = method;
if (this.isAuthzAnnotationPresent(method)) {
return true;
} else {
if (targetClass != null) {
try {
m = targetClass.getMethod(m.getName(), m.getParameterTypes());
return this.isAuthzAnnotationPresent(m) || this.isAuthzAnnotationPresent(targetClass);
} catch (NoSuchMethodException var5) {
}
}
return false;
}
}
private boolean isAuthzAnnotationPresent(Method method) {
Class[] var2 = AUTHZ_ANNOTATION_CLASSES;
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
Class<? extends Annotation> annClass = var2[var4];
Annotation a = AnnotationUtils.findAnnotation(method, annClass);
if (a != null) {
return true;
}
}
return false;
}
}
总结
AuthorizationAttributeSourceAdvisor匹配所有类,匹配所有加了认证注解的方法