权限校验
cool-admin for java
采用spring-security与 jwt 作为框架安全与权限校验。
原理
1、系统启动的时候,系统会查询并向spring-security加载所有设置的权限;
modules/base/security/MySecurityMetadataSource.java
package com.cooljs.modules.base.security;
import com.cooljs.modules.base.service.sys.BaseSysPermsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
/**
* 权限资源管理器
* 为权限决断器提供支持
*/
@Slf4j
@Component
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Resource
private BaseSysPermsService baseSysPermsService;
private Map<String, Collection<ConfigAttribute>> map = null;
/**
* 加载权限表中所有操作请求权限
*/
public void loadResourceDefine() {
map = new HashMap<>();
Collection<ConfigAttribute> configAttributes;
ConfigAttribute cfg;
String[] perms = baseSysPermsService.getAllPerms();
// 获取启用的权限操作请求
for (String perm : perms) {
configAttributes = new ArrayList<>();
cfg = new SecurityConfig(perm);
//作为MyAccessDecisionManager类的decide的第三个参数
configAttributes.add(cfg);
//用权限的path作为map的key,用ConfigAttribute的集合作为value
map.put(perm.replaceAll(":", "/"), configAttributes);
}
}
/**
* 判定用户请求的url是否在权限表中
* 如果在权限表中,则返回给decide方法,用来判定用户是否有此权限
* 如果不在权限表中则放行
*
* @param o
* @return
* @throws IllegalArgumentException
*/
@Override
public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
if (map == null) {
loadResourceDefine();
}
//Object中包含用户请求request
String url = ((FilterInvocation) o).getRequestUrl();
return map.get(url.replace("/admin/", "").split("[?]")[0]);
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return new ArrayList<>();
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
}
2、用户登录,查询该用户所具有的权限并缓存;
modules/base/security/JwtUserDetailsServiceImpl.java
package com.cooljs.modules.base.security;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cooljs.core.cache.CoolCache;
import com.cooljs.core.security.jwt.JwtUser;
import com.cooljs.modules.base.entity.sys.BaseSysUserEntity;
import com.cooljs.modules.base.service.sys.BaseSysPermsService;
import com.cooljs.modules.base.service.sys.BaseSysUserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* 获得用户信息
*/
@Component
public class JwtUserDetailsServiceImpl implements UserDetailsService {
@Resource
private BaseSysUserService baseSysUserService;
@Resource
private BaseSysPermsService baseSysPermsService;
@Resource
private CoolCache coolCache;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
BaseSysUserEntity sysUserEntity = baseSysUserService.getOne(Wrappers.<BaseSysUserEntity>lambdaQuery()
.eq(BaseSysUserEntity::getUsername, username)
.eq(BaseSysUserEntity::getStatus, 1));
if (ObjectUtil.isEmpty(sysUserEntity)) {
throw new UsernameNotFoundException("用户名不存在");
}
List<GrantedAuthority> authority = new ArrayList<>();
String[] perms = baseSysPermsService.getPerms(sysUserEntity.getId());
for (String perm : perms) {
authority.add(new SimpleGrantedAuthority(perm));
}
Long[] departmentIds = baseSysPermsService.getDepartmentIdsByRoleIds(sysUserEntity.getId());
JwtUser jwtUser = new JwtUser(sysUserEntity.getUsername(), sysUserEntity.getPassword(), authority, sysUserEntity.getStatus() == 1);
Long[] roleIds = baseSysPermsService.getRoles(sysUserEntity);
coolCache.set("admin:userDetails:" + jwtUser.getUsername(), jwtUser);
coolCache.set("admin:passwordVersion:" + sysUserEntity.getId(), sysUserEntity.getPasswordV());
coolCache.set("admin:userInfo:" + sysUserEntity.getId(), sysUserEntity);
coolCache.set("admin:department:" + sysUserEntity.getId(), departmentIds);
coolCache.set("admin:roleIds:" + sysUserEntity.getId(), roleIds);
return jwtUser;
}
}
3、每次调用接口的时候spring-security权限拦截器校验当前用户权限;
core/security/MyFilterSecurityInterceptor.java
package com.cooljs.core.security;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.*;
import java.io.IOException;
/**
* 权限管理拦截器
* 监控用户行为
*/
@Slf4j
@Component
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Resource
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
FilterInvocation fi = new FilterInvocation(request, response, chain);
invoke(fi);
}
public void invoke(FilterInvocation fi) throws IOException, ServletException {
InterceptorStatusToken token = super.beforeInvocation(fi);
try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
@Override
public void destroy() {
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
}
当前用户
- 获得用户名
@Resource
private CoolSecurityUtil coolSecurityUtil;
String username = coolSecurityUtil.username();
- 获得用户信息
请求过程中会解析 token,并将用户信息存在整个请求过程中的requestParams
@Resource
private CoolSecurityUtil coolSecurityUtil;
JSONObject userInfo = coolSecurityUtil.userInfo(requestParams);
userInfo
字段 | 类型 | 说明 |
---|---|---|
roleIds | Long[] | 角色数组 |
username | String | 用户名 |
userId | Long | 用户ID |
passwordVersion | Integer | 密码版本号 |