Spring Security 可以使用ACL进行数据的权限访问,但是ACL过于复杂,对于简单的数据权限,我们只需要在查询方法上进行数据过滤就可以了。
自定义PermissionEvaluator
import java.io.Serializable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
@Configuration
public class CustomPermissionEvaluator implements PermissionEvaluator {
private static final Logger log = LoggerFactory.getLogger(CustomPermissionEvaluator.class);
//普通的targetDomainObject判断
@Override
public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
log.error("-------------------------------------");
return false;
}
//用于ACL的访问控制
@Override
public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
log.error("-------------------------------------");
return false;
}
}
配置GlobalMethodSecurityConfiguration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
private CustomPermissionEvaluator customPermissionEvaluator;
@Autowired
public void setCustomPermissionEvaluator(CustomPermissionEvaluator customPermissionEvaluator) {
this.customPermissionEvaluator = customPermissionEvaluator;
}
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
expressionHandler.setPermissionEvaluator(customPermissionEvaluator);
return expressionHandler;
//return super.createExpressionHandler();
}
}
需要注意的是,项目中不能有多个EnableGlobalMethodSecurity注解,不然会根据顺序加载不同导致未能正确加载自定义的config
权限验证 (PreAuthorize/PostAuthorize(EL中获取返回值:returnObject))
/**
* 修改密码.
* @return
*/
@PostMapping("/modifyPassword")
@PreAuthorize("hasPermission(#baseReq,'read')")
public ResponseEntity<BaseResponse> modifyPassword(@Valid @RequestBody BaseRequest<ModifyPasswordReq> baseReq, HttpServletRequest request) {
Principal principal = SecurityContextHolder.getContext().getAuthentication();
Map<String, Object> data = new HashMap<>();
data.put("code", "fail");
。。。
return BaseResponse.success(data);
}
这样的话,在CustomPermissionEvaluator的第一个方法会被调用,并传入参数,第一个是Security的authentication,第二个参数是就baseRequest对象,第三个参数是read。方法中获取到这三个参数可以判断,
- 如果有authentication中有read权限,返回true
- 如果baseRequest中的某个字段比如createid和authentication的id相同表示有权限,返回true
- 其他,返回false
过滤(PreFilter/PostFilter(EL中获取返回值:filterObject))
@GetMapping("/list")
@PostFilter("hasPermission(filterObject, 'read')")
public List<User> list(){
return jpa.findAll();
}
在自定义PermissionEvaluator,修改下判断方法,当返回false时,spring会把对象从list<user>移除
if(targetDomainObject.getClass().isAssignableFrom(User.class)){
User u = (User) targetDomainObject;
if(u.getLogin().equals(authentication.getName())){
return true;
}
}
log.error("-------------------------------------");
return false;