目录
简述
核心类
一、Action 类
二、Menu 类
三、Role 类
分析
权限配置定义和读取
一、Action配置
二、菜单配置
三、配置读取生成权限表
提供接口配置角色
权限校验
登录
结尾
哈哈哈哈哈哈,我答应会经常更新的。很好,再一次做不到,打脸!来来来写写博客吹吹牛逼来了!
是这样的目前很多业务系统都希望系统的权限是可以动态可调整的,而且还希望系统的权限可以精确到按钮级别的。通常会这样做先做个按钮和菜单的功能配置表,去控制前端按钮的显示与否。然后再做一个接口管理表,管理所有接口在某个角色当中是否有权限调用。而且我们需要通过前端页面去配置这些接口和按钮。
今天就说一下我是怎么去实现,不需要去配置有什么接口,有什么按钮的动态权限,同时我也会基于AOP去实现权限认证。
主要是记录系统里面有多少个操作,每个接口就算一个操作,我们将功能按钮和对应的接口抽象成一个Action 每一个Action都需要定义一个全局唯一的ActionId,Action还关联着对应的菜单 menuId(主要生成权限树)。
@Entity
@Table(name = "T_ACTION")
public class Action {
public static final int STATUS_ENABLE = 1;
public static final int STATUS_DISABLE = 0;
/**
* 行为ID 全局唯一 英文标识
*/
@Id
@Column(nullable = false)
private String actionId;
/**
* 行为名称 主要用作显示
*/
@Column(nullable = false)
private String actionName;
/**
* 行为对应的接口URI
*/
@Column(nullable = false, length = 256)
private String actionUri;
/**
* 行为所属的菜单ID
*/
@Column(nullable = false)
private String menuId;
@Column(nullable = false, length = 6)
private int status;
}
菜单对象,主要用于是否需要显示前端菜单项的。判断可以通过菜单以及子孙菜单是否有相应的action权限,如果当前角色 一个action的权限都没有那就直接将菜单隐藏,这样在返回给前端的权限树当中,就不会有相应的菜单。
Entity
@Table(name = "T_MENU")
public class Menu {
public static final int STATUS_ENABLE = 1;
public static final int STATUS_DISABLE = 0;
public Menu() {
}
public Menu(String menuId, String menuName, String parentMenuId) {
this.menuId = menuId;
this.menuName = menuName;
this.parentMenuId = parentMenuId;
this.status = STATUS_ENABLE;
}
/**
* 菜单ID 全局唯一标识
*/
@Id
@Column(nullable = false)
private String menuId;
/**
* 菜单名称 用于显示
*/
@Column(nullable = false)
private String menuName;
/**
* 父菜单ID 如果没有父级菜单ID 即为顶级菜单
*/
private String parentMenuId;
/**
* 权限状态如果为0则功能已经不存在 或者 已经被取消权限判断
*/
@Column(nullable = false, length = 6)
private int status;
}
Role 主要是配置和管理有那些求权限,同时就是关联着相应的管理员用户。
@Entity
@Table(name = "T_ROLE")
public class Role {
public static final int STATUS_DISABLE = 0;
public static final int STATUS_ENABLE = 1;
@Id
@GeneratedValue
@Column(nullable = false)
private Long roleId;
/**
* 角色
*/
@Column(nullable = false)
private String name;
/**
* 角色描述
*/
@Column(nullable = false,length = 300)
private String description;
/**
* 角色状态
*/
@Column(nullable = false)
private int status;
/**
* 角色所对应的权限
*/
@ManyToMany(targetEntity = Action.class)
@JoinTable(name = "T_ACTION_ROLE", joinColumns = {@JoinColumn(name = "ROLE_ID")}, inverseJoinColumns = {@JoinColumn(name = "ACTION_ID")})
private Set actions;
@OneToMany(mappedBy = "role")
private Set adminUsers;
/**
* 创建角色的时间
*/
@Column(nullable = false)
private Date createTime;
}
目前我们要做到动态权限要做到几个地方:
我们会定义一个annotation,通过annotation配置到对应的接口上,用作actionId定义和AOP校验的切入点。
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Permission {
String actionId();
String menuId();
/**
* 功能名称
* @return
*/
String name();
}
另外还有一个是不检查权限,只是需要当前用户一定是已经登录并且可以获得其用户信息。我将会定义另外一个annotation进行判断
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Authorization {
}
定义完成这两个annotation之后,我将使用这两个annotation定义在ResController对应的方法当中。使用方法如下:
@RestController
@RequestMapping("/decorate")
public class DecorateController {
@Autowired
private IDecorateService decorateService;
@GetMapping("/view")
@Permission(actionId = "decorateManage_viewBtn", menuId = "decorateManage", name = "查看小程序装修")
public ResponseBasic view() {
return ResponseBasic.ok(decorateService.view());
}
}
可以看到我在/view 这个接口上面,定义了一个名称为 查看小程序装修 actionId 为 decorateManage_viewBtn 属于 decorateManage 菜单的权限,这个权限将会是全局唯一的。同时我们这个actionId 在用户登录的时候用户拥有什么权限actionId 会全部给前端渲染页面。
我们需要定义个菜单配置的类,其实只是配置一个bean将所有配置的菜单保存起来
/**
* Created by TONY YAN
*/
public class PermissionConfig {
private Set
然后就开始配置菜单,在其中一个配置类里面配置定义好的PermissionConfig,定义好各个菜单的名称和menuId,还有就是就是父级的menuId
@Bean
public PermissionConfig menuDefinition() {
PermissionConfig permissionConfig = new PermissionConfig();
permissionConfig.addMenu(new Menu("staffManageIndex", "员工管理", null));
permissionConfig.addMenu(new Menu("roleManage", "角色管理", "staffManageIndex"));
permissionConfig.addMenu(new Menu("staffManage", "员工管理", "staffManageIndex"));
permissionConfig.addMenu(new Menu("storeManage", "门店管理", "staffManageIndex"));
permissionConfig.addMenu(new Menu("orderManageIndex", "订单管理", null));
permissionConfig.addMenu(new Menu("orderSurvey", "订单概况", "orderManageIndex"));
permissionConfig.addMenu(new Menu("orderManage", "所有订单", "orderManageIndex"));
permissionConfig.addMenu(new Menu("goodsManage", "商品管理", null));
permissionConfig.addMenu(new Menu("memberManage", "会员管理", null));
permissionConfig.addMenu(new Menu("decorateManage", "小程序装修", null));
permissionConfig.addMenu(new Menu("cardManage", "礼品卡管理", null));
return permissionConfig;
}
OK,那我们还需要提供一个借口给前端用户,选择某个角色应该拥有什么权限。所以我们需要每次启动SpringBoot应用的时候,将所有其本身定义的所有权限action给记录起来。需要完成这一步的思路是这样的,我们需要在SpringBoot启动后,然后扫描Spring容器里面所有的Bean找到对应的@Permission的配置。上代码:
1、我们需要实现initializingBean接口,在afterPropertiesSet 方法里面获得Spring的context,然后通过获得所有的RestController类的Bean。
/**
* Created by TONY YAN
*/
@Component
public class PermissionInitializing implements InitializingBean {
@Autowired
private ApplicationContext context;
@Autowired
private PermissionService permissionService;
@Override
public void afterPropertiesSet() throws Exception {
Map beans = this.context.getBeansWithAnnotation(RestController.class);
this.permissionService.permissionInit(beans.values());
}
}
2、permissionInit这个方法就是读取配置的关键
/**
* Created by TONY YAN
*/
@Service
@Transactional
public class PermissionService {
@Autowired
private ActionRepository actionRepository;
@Autowired
private MenuRepository menuRepository;
@Autowired
private PermissionConfig permissionConfig;
@Autowired
private JdbcTemplate jdbcTemplate;
private Logger logger = LoggerFactory.getLogger(PermissionService.class);
/**
* 权限表自动化生成
* @param beans
* @throws Exception
*/
public void permissionInit(Collection
虽然有点长但是还是算比较简单,然后说说这个permissionInit方法干了什么。首先将以前旧的所有Action和Menu表的数据设置成无效,然后开始迭代所有bean,在这里有个地方需要注意的Object targetObj = ProxyUtils.getTarget(obj); 。其中ProxyUtils的类后面会贴出来,意思是这样的,首先我们在Spring容器获得的所有Bean基本上都是被CGLib 或者 JdkDynamicProxy代理过的,所以我们这边获得的所有都是代理对象,导致我们获得class对象都是一个代理类,导致我们没有办法读取到原始类里面的@permission配置。所以通过ProxyUtils 获得真实的原来对象。
然后通过ingestActionList 方法读取出类里面的所有@Permission配置,然后通过ambiguityActionCheck方法检查其权限是否冲突,如果冲突就抛出异常(消息可以看看其代码),然后将权限保存到表当中,由于生成的权限status都是在生效状态,等于如果是上次启动的时候已经存在的权限,在这次保存的时候得到更新从失效改成了生效,而新的权限记录将会插入一条数据。
通过PermissionConfig 对象(之前配置过的),获得所有的菜单对象,并且保存。也是同样道理以前已经存在的菜单对象在这次从新保存的时候得到了更新从一个开始的失效状态,更新成生效状态。
检查@Permission配置的Action定义的所属菜单是否存在,如果没有对应menuId的菜单对象,就给出warning的日志消息。
然后将所有状态为失效的Action和Menu记录删除,保持最终的权限条目结果。然后完成初始化,下面帖出ProxyUtils的代码:
public class ProxyUtils {
public static Object getTarget(Object proxy) throws Exception {
if (!AopUtils.isAopProxy(proxy)) {
return proxy;
}
if (AopUtils.isJdkDynamicProxy(proxy)) {
return getJdkDynamicProxyTargetObject(proxy);
} else {
return getCglibProxyTargetObject(proxy);
}
}
private static Object getCglibProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0");
h.setAccessible(true);
Object dynamicAdvisedInterceptor = h.get(proxy);
Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget();
return target;
}
private static Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception {
Field h = proxy.getClass().getSuperclass().getDeclaredField("h");
h.setAccessible(true);
AopProxy aopProxy = (AopProxy) h.get(proxy);
Field advised = aopProxy.getClass().getDeclaredField("advised");
advised.setAccessible(true);
Object target = ((AdvisedSupport) advised.get(aopProxy)).getTargetSource().getTarget();
return target;
}
}
目前已经拥有菜单和功能的基础条目了,先就差某个角色拥有什么功能这样的一个功能了。首先我们需要提供一个功能让前端获得整体一个权限树构成,就是说有那些菜单,里面有什么权限和什么子菜单这样一个结构。其中我们需要提供所有全局权限树,还有就是角色的权限树,还有就是用户的权限树等等。这里就简单贴出代码,不算是这里的核心思想。只要清楚数据库结构,就能简单实现界面所需要的接口。
@Service
public class PermissionTreeService {
@Autowired
private ActionRepository actionRepository;
@Autowired
private MenuRepository menuRepository;
@Autowired
private PermissionConfig permissionConfig;
@Autowired
private JdbcTemplate jdbcTemplate;
private Logger logger = LoggerFactory.getLogger(PermissionTreeService.class);
/**
* 获得角色所有的权限actionID
* @param role
* @return
*/
public Set getUserActionIdList(Role role) {
if (role == null) {
return new HashSet<>();
}
Set actions = role.getActions();
Set actionIds = actions.stream().map(action -> action.getActionId()).collect(Collectors.toSet());
return actionIds;
}
/**
* 获得用户所有的权限actionID
* @param adminUser
* @return
*/
public Set getUserActionIdList(AdminUser adminUser) {
if (adminUser == null) {
return new HashSet<>();
}
Set actions = null;
if (adminUser.getSuperAdmin() == 99) {
actions = this.actionRepository.findAll().stream().collect(Collectors.toSet());
} else {
actions = adminUser.getRole().getActions();
}
Set actionIds = actions.stream().map(action -> action.getActionId()).collect(Collectors.toSet());
return actionIds;
}
/**
* 获得用户的权限树
* @param adminUser
* @return
*/
public List userPermissionTree(AdminUser adminUser) {
List menuVos = this.getActionTree(adminUser);
return this.menuFilterLayer(menuVos);
}
/**
* 获得角色用户的权限树
* @param role
* @return
*/
public List userPermissionTree(Role role) {
List menuVos = this.getActionTree(role);
return this.menuFilterLayer(menuVos);
}
/**
* 菜单过滤器层
* @param menuVos
* @return
*/
public List menuFilterLayer(List menuVos) {
List result = new ArrayList<>();
for (MenuVo menuVo : menuVos) {
if (hasMenusPermission(menuVo)) {
if (menuVo.getSubMenus() != null && menuVo.getSubMenus().size() > 0) {
menuVo.setSubMenus(menuFilterLayer(menuVo.getSubMenus()));
}
menuVo.setActions(actionFilterLayer(menuVo.getActions()));
result.add(menuVo);
}
}
return result;
}
/**
* action过滤器层
* @param actionList
* @return
*/
public List actionFilterLayer(List actionList) {
List result = actionList.stream().filter(actionVo -> actionVo.getHasPermission() == 1).collect(Collectors.toList());
return result;
}
public boolean hasMenusPermission(MenuVo menuVo) {
List actionVos = menuVo.getActions();
for (ActionVo actionVo : actionVos) {
if (actionVo.getHasPermission() != 0) {
return true;
}
}
if (menuVo.getSubMenus() != null && menuVo.getSubMenus().size() > 0) {
for (MenuVo subVo : menuVo.getSubMenus()) {
if (hasMenusPermission(subVo)) {
return true;
}
}
}
return false;
}
public List getActionTree(Role role) {
Set userActionSet = this.getUserActionIdList(role);
return getActionTreeCore(userActionSet);
}
public List getActionTree(AdminUser adminUser) {
Set userActionSet = this.getUserActionIdList(adminUser);
return getActionTreeCore(userActionSet);
}
public List getActionTreeCore(Set userActionSet) {
List menus = this.menuRepository.findAll();
List actions = this.actionRepository.findAll();
List vos = new ArrayList<>();
for (Menu menu : menus) {
if (menu.getParentMenuId() == null) {
MenuVo vo = new MenuVo();
CommonUtils.copyProperty(menu, vo);
findSubMenus(vo, menus, actions, userActionSet);
findActionInMenu(vo, actions, userActionSet);
vos.add(vo);
}
}
return vos;
}
public void findActionInMenu(MenuVo menuVo, List actions, Set userActionIdSet) {
List vos = new ArrayList<>();
for (Action action : actions) {
if (action.getMenuId().equals(menuVo.getMenuId())) {
ActionVo actionVo = new ActionVo();
CommonUtils.copyProperty(action, actionVo);
actionVo.setHasPermission(userActionIdSet.contains(action.getActionId()) ? 1 : 0);
vos.add(actionVo);
}
}
menuVo.setActions(vos);
}
/**
* 获得子菜单单
* @param menuVo
* @param menus
* @param actions
* @param userActionIdSet
*/
public void findSubMenus(MenuVo menuVo, List menus, List actions, Set userActionIdSet) {
List subMenuVoList = new ArrayList<>();
for (Menu menu : menus) {
if (menu.getParentMenuId() == null) {
continue;
}
if (menu.getParentMenuId().equals(menuVo.getMenuId())) {
MenuVo subVo = new MenuVo();
CommonUtils.copyProperty(menu, subVo);
subMenuVoList.add(subVo);
}
}
if (subMenuVoList.size() > 0) {
menuVo.setSubMenus(subMenuVoList);
for (MenuVo vo : subMenuVoList) {
findActionInMenu(vo, actions, userActionIdSet);
findSubMenus(vo, menus, actions, userActionIdSet);
}
}
}
}
提供具体的权限和角色绑定的接口这个也是比较简单的业务了,下面也简单贴出来一下。哎哟提醒一下我这里用了大量的深拷贝。
public ResponseBasic createRole(ReqRoleVo req) {
Role role = new Role();
if (this.roleRepository.findByName(req.getName()) != null) {
return ResponseBasic.error(ResponseBasic.ERROR_CODE, "角色名称名已经存在");
}
role.setCreateTime(new Date());
req.setRoleId(null);
CommonUtils.copyProperty(req, role);
role.setStatus(Role.STATUS_ENABLE);
Set actions = this.actionRepository.findByActionIdIn(req.getActionIdList());
role.setActions(actions);
this.roleRepository.save(role);
return ResponseBasic.ok();
}
public ResponseBasic getRoleDetail(ReqGetRole req) {
Role role = this.roleRepository.findOne(req.getRoleId());
RoleDetailVo detailVo = new RoleDetailVo();
CommonUtils.copyProperty(role, detailVo);
List permissionTree = this.permissionTreeService.getActionTree(role);
detailVo.setPermissionTree(permissionTree);
return ResponseBasic.ok(detailVo);
}
public ResponseBasic> getPermissionTree() {
List permissionTree = this.permissionTreeService.getActionTreeCore(new HashSet<>());
return ResponseBasic.ok(permissionTree);
}
public ResponseBasic> getRoleList(ReqSearchRole req) {
Page page = this.roleRepository.findAll(new PageRequest(req.getPageNo() - 1, req.getPageSize()));
Page vos = page.map(new Converter() {
@Override
public SimpleRoleVo convert(Role role) {
SimpleRoleVo vo = new SimpleRoleVo();
CommonUtils.copyProperty(role, vo);
vo.setStatusName(role.getStatus() == 0 ? "禁用" : "正常");
vo.setNumOfUser(userRepository.countByRole(role));
return vo;
}
});
return ResponseBasic.ok(new AppPage<>(vos));
}
public ResponseBasic updateRole(ReqRoleVo req) {
Role role = this.roleRepository.findOne(req.getRoleId());
if (role == null) {
return ResponseBasic.error(ResponseBasic.ERROR_CODE, "角色不存在");
}
CommonUtils.copyProperty(req, role);
Set actions = this.actionRepository.findByActionIdIn(req.getActionIdList());
role.setActions(actions);
this.roleRepository.save(role);
return ResponseBasic.ok();
}
public ResponseBasic> getSimpleRoleList() {
List roles = this.roleRepository.findAll(new Sort("createTime"));
if (roles != null) {
List roleItemVos = roles.stream().map(role -> {
RoleItemVo itemVo = new RoleItemVo();
itemVo.setRoleId(role.getRoleId());
itemVo.setRoleName(role.getName());
return itemVo;
}).collect(Collectors.toList());
return ResponseBasic.ok(roleItemVos);
}
return ResponseBasic.ok(new ArrayList<>());
}
public ResponseBasic> getRoleListPermissionTree() {
List roles = this.roleRepository.findAll(new Sort(Sort.Direction.DESC, "createTime"));
List roleListItemVoList = new ArrayList<>();
for (Role role : roles) {
RoleListItemVo listItemVo = new RoleListItemVo();
CommonUtils.copyProperty(role, listItemVo);
listItemVo.setRoleName(role.getName());
listItemVo.setPermissionTree(this.permissionTreeService.userPermissionTree(role));
roleListItemVoList.add(listItemVo);
}
return ResponseBasic.ok(roleListItemVoList);
}
说了那么多应该都到权限校验这一部分了,首选我们是通过AOP实现权限校验的。同时我们使用redis保存用户的Token令牌,然而我们前端会通过Http header 的authorization 参数将token带会来进行校验。而且我们会分成这几部分去完成整一个权限校验。
好啦,就是核心的几个类。我这边贴出来。。。
第一个就是AuthInterceptor 和 AuthService,其中AuthService支持在任何地方进行注入,通过他可以获得当前登录的用户对象。
/**
* Created by TONY YAN
*/
@Component
public class AuthInterceptor implements HandlerInterceptor {
@Autowired
private AuthService authService;
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
this.authService.setAdminUserInCache(httpServletRequest);
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
this.authService.removeAdminUserInCahce();
}
}
AuthService类其中在获得用户令牌的方法当中,也会调用到PermissionTreeService的方法去获得当前用户的角色权限树和ActionId列表,详细请看代码注释。
public class AuthService {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private PermissionTreeService permissionTreeService;
public static long expireDays = 20;
private static ThreadLocal USER_THREAD_LOCAL = new ThreadLocal<>();
/**
* 将根据token获得用户对象并存储到threadLocal当中
*
* @param request
*/
public void setAdminUserInCache(HttpServletRequest request) {
String token = request.getParameter("Authorization");
if (StringUtils.isEmpty(token)) {
token = request.getHeader("Authorization");
}
if (StringUtils.isEmpty(token)) {
return;
}
AuthToken user = this.getUserByToken(token);
if (user == null) {
return;
}
user.setToken(token);
USER_THREAD_LOCAL.set(user);
}
public void removeAdminUserInCahce() {
USER_THREAD_LOCAL.remove();
}
/**
* 获得用户令牌
*
* @param user
* @return
*/
public AuthToken getUserTokenAndSetUser(AdminUser user) {
ValueOperations valOps = this.redisTemplate.opsForValue();
String token = UUID.randomUUID().toString();
String key = this.getRedisUserKey(token);
AuthToken authToken = new AuthToken();
CommonUtils.copyProperty(user, authToken);
authToken.setActionIds(this.permissionTreeService.getUserActionIdList(user));
authToken.setMenuVos(this.permissionTreeService.userPermissionTree(user));
authToken.setToken(token);
valOps.set(key, authToken, expireDays, TimeUnit.MINUTES);
return authToken;
}
/**
* 获得缓存用户
*
* @return
*/
public AuthToken getUserByToken() {
return USER_THREAD_LOCAL.get();
}
/**
* 获得缓存用户
*
* @param userToken
* @return
*/
public AuthToken getUserByToken(String userToken) {
ValueOperations valOps = this.redisTemplate.opsForValue();
String key = this.getRedisUserKey(userToken);
AuthToken user = valOps.get(key);
if (user != null) {
this.redisTemplate.expire(key, expireDays, TimeUnit.DAYS);
}
return user;
}
/**
* 从redis当中移除用户
*/
public void removeAdminUserInRedis() {
AuthToken user = this.getUserByToken();
if (user == null) {
return;
}
String key = this.getRedisUserKey(user.getToken());
this.redisTemplate.delete(key);
}
/**
* 获得用户token缓存的key
*
* @param userToken
* @return
*/
public String getRedisUserKey(String userToken) {
return PermissionContacts.ADMIN_USER_TOKEN + userToken;
}
}
然后就是核心的PermissionAspect 类,主要是做相关的拦截权限校验操作。然后你会看到我偷鸡,只要获得当前用户的superAdmin字段是99的话,我就会赋予全部权限。这是为什么了,主要是系统在初始化的时候没有用户,那这样也会妨碍到角色的配置问题,所以才在这个位置做出取巧的一个方法,当首次配置后,直接将这个用户的superAdmin去掉然后绑定到其中一个超级管理员权限的角色即可。
/**
* Created by TONY YAN
*/
@Component
@Aspect
public class PermissionAspect {
@Autowired
private AuthService authService;
/**
* 拦截所有@Permission的方法,检查其当前用户是否有权限访问
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(com.lotus.admin.permission.annotation.Permission)")
public Object check(ProceedingJoinPoint joinPoint) throws Throwable {
AuthToken authToken = this.authService.getUserByToken();
if (authToken == null) {
return ResponseBasic.error(ResponseBasic.AUTH_ERROR_CODE, "用户未登陆");
}
String actionId = this.getPermissionMethod(joinPoint).getAnnotation(Permission.class).actionId();
boolean ok = false;
if (authToken.getSuperAdmin() == 99) {
ok = true;
} else {
if (authToken.getActionIds().contains(actionId)) {
ok = true;
}
}
if (ok) {
return joinPoint.proceed(joinPoint.getArgs());
}
return ResponseBasic.error(ResponseBasic.PERMISSION_ERROR_CODE, "你没有权限使用该功能");
}
/**
* 拦截所有@Authorization的方法,检查当前调用方法的线程是否已经获得其用户对象,如果没有则未登录
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(com.lotus.admin.permission.annotation.Authorization)")
public Object authCheck(ProceedingJoinPoint joinPoint) throws Throwable {
AuthToken authToken = this.authService.getUserByToken();
if (authToken == null) {
return ResponseBasic.error(ResponseBasic.AUTH_ERROR_CODE, "用户未登陆");
}
return joinPoint.proceed(joinPoint.getArgs());
}
/**
* 获得方法对象
* @param joinPoint
* @return
* @throws NoSuchMethodException
*/
private Method getPermissionMethod(ProceedingJoinPoint joinPoint) throws NoSuchMethodException {
Signature sig = joinPoint.getSignature();
MethodSignature msig = null;
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
msig = (MethodSignature) sig;
Object target = joinPoint.getTarget();
Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
return currentMethod;
}
}
其实我不打算在这里贴出登录的代码的,也没有什么好贴出来的。反正AuthService已经有获得令牌的代码了,就差用户名校验这些逻辑了估计大家都会了,我就不打算贴出了其实登录这里的核心代码就是调用了AuthService的public AuthToken getUserTokenAndSetUser(AdminUser user) 方法而已。
这个权限框架,也是解决了很多业务上的痛点。也算是写了这么多年代码的一个经验总结吧。其实还是有很多需要进步的东西的,因为也没有写多久才用了一周不到的时间完成。其中有如下的优化想法:
好啦,有什么就留言我吧。。。绝对原创转载请注明出处。。。