都像这样写,然后在数据库写添加这个xxxxxx
权限,在配置角色时把这个xxxxxx
权限给配置对应角色,当用户登录后,访问这个接口,就会验证用户是否有这个权限
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResourcePermission {
String value() default "";
}
@Slf4j
public class ResourcePermissionConfig implements InitializingBean {
@Autowired
private WebApplicationContext applicationContext;
@Value("${spring.application.name}")
private String applicationName;
@Autowired
private PermissionProducer permissionProducer;
@Getter
@Setter
private List<PermissionEntityVO> permissionEntities = Lists.newArrayList();
@Override
public void afterPropertiesSet(){
log.info("===============ResourcePermissionConfig==============");
RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
map.keySet().forEach(mappingInfo -> {
HandlerMethod handlerMethod = map.get(mappingInfo);
ResourcePermission method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), ResourcePermission.class);
Optional.ofNullable(method)
.ifPresent(resourcePermission -> mappingInfo
.getPatternsCondition()
.getPatterns()
.forEach(url -> {
String strUrl = URLConvertUtil.capture(url);
String permission = URLConvertUtil.convert(url);
permissionEntities.add(PermissionEntityVO
.builder()
.name(method.value())
.permission(permission)
.serviceId(applicationName)
.url(strUrl)
.build());
}));
ResourcePermission controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), ResourcePermission.class);
Optional.ofNullable(controller)
.ifPresent(resourcePermission -> mappingInfo
.getPatternsCondition()
.getPatterns()
.forEach(url -> {
String strUrl = URLConvertUtil.capture(url);
String permission = URLConvertUtil.convert(url);
if(StrUtil.isNotBlank(permission)){
permissionEntities.add(PermissionEntityVO
.builder()
.name(method.value())
.permission(permission)
.serviceId(applicationName)
.url(strUrl)
.build());
}
}));
});
if(CollUtil.isNotEmpty(permissionEntities)){
permissionProducer.send(permissionEntities);
}
}
}
这里为什么使用了mq 把数据发出去?因为在微服务中,像这种代码不可能每个服务需要设置权限都重新写一套吧,所以要创建一个公共模块,然后把这代码写在公共模块中,其他服务需要配置权限时,直接依赖即可
@Slf4j
@AllArgsConstructor
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private final RemoteTokenServices remoteTokenServices;
private final RestTemplate restTemplate;
private final ResourceAuthExceptionEntryPoint resourceAuthExceptionEntryPoint;
private final CustomAccessDeniedHandler customAccessDeniedHandler;
private final AuthIgnoreConfig authIgnoreConfig;
private final ResourcePermissionConfig resourcePermissionConfig;
@Override
public void configure(HttpSecurity http) throws Exception {
String[] urls = authIgnoreConfig.getIgnoreUrls().stream().distinct().toArray(String[]::new);
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
registry.antMatchers(urls).permitAll();
resourcePermissionConfig.getPermissionEntities().forEach(
permissionEntity -> registry.antMatchers(permissionEntity.getUrl()).hasAnyRole(permissionEntity.getPermission()));
registry.anyRequest().authenticated().and().csrf().disable();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
UserAuthenticationConverter userTokenConverter = new CustomUserAuthenticationConverter();
accessTokenConverter.setUserTokenConverter(userTokenConverter);
remoteTokenServices.setRestTemplate(restTemplate);
remoteTokenServices.setAccessTokenConverter(accessTokenConverter);
resources.authenticationEntryPoint(resourceAuthExceptionEntryPoint);
resources.accessDeniedHandler(customAccessDeniedHandler);
resources.tokenServices(remoteTokenServices);
}
}
@Slf4j
@Component
@RabbitListener(queues = RabbitMQConstants.PERMISSION_QUEUE)
@AllArgsConstructor
public class PermissionListener {
private final SysPermissionService sysPermissionService;
/**
* 消息消费
*/
@RabbitHandler
public void recieved(List<PermissionEntityVO> list) {
log.info("===========PermissionListener收到消息=============");
if(ObjectUtil.isNotNull(list)){
sysPermissionService.updateSysPermission(list);
}
}
}
MQ 收到消息后保存到数据库
@Slf4j
@Service
@AllArgsConstructor
public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
private final AuthenticationUserService customUserService;
private final SysUserRoleMapper sysUserRoleMapper;
private final SysPermissionMapper sysPermissionMapper;
@Override
@Cacheable(value = SecurityConstants.CACHE_USER_DETAILS, key = "#user_details", unless = "#result == null")
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
SysUser sysUser = customUserService.loadUserByUsername(username);
//查询用户有哪些角色
List<Long> roleIds = sysUserRoleMapper
.selectList(Wrappers.<SysUserRole>query().lambda().eq(SysUserRole::getUserId,sysUser.getUserId()))
.stream()
.map(SysUserRole::getRoleId)
.collect(Collectors.toList());
if(CollUtil.isNotEmpty(roleIds)){
//根据角色查询权限
List<String> roleCodes = sysPermissionMapper.getPermission(roleIds)
.stream()
.map(SysPermission::getPermission)
.collect(Collectors.toList());
sysUser.setRoleCode(roleCodes);
}
UserDetails userDetails = UserDetailsUtils.getUserDetails(sysUser);
return userDetails;
}
}
@ResourcePermission("调用sms服务")
@RequestMapping(value = "/sms",method = RequestMethod.GET)
public R getSms(){
return remoteSmsService.hello();
}