先置条件(基于我的项目) 假设我现在 有gateway-service(网关) auth-service(权限认证) game-service(游戏) ad-service (广告)
<!--springboot 版本-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-cloud.version>Hoxton.SR3</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.0.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!--整合spring cloud-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--整合spring cloud alibaba-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
1.game-service 通过feign调用 ad-service服务 现在要细粒度控制权限 采用OAuth2 来实现 首先4个微服务 都添加如下依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
2.gateway-service(网关) game-service(游戏) ad-service (广告) yml 配置文件添加配置
security:
oauth2:
client:
#client-id client-secret 请根据自己不同的服务 填写配置信息
client-id: gateway_client
client-secret: 123456
resource:
jwt:
#这个是认证服务器地址 即auth-service
key-uri: http://localhost:8032/oauth/token_key #访问路径参考源码 org.springframework.security.oauth2.provider.endpoint
3.实现微服务之间权限控制
//在需要控制的方法上添加注解
@PreAuthorize("#oauth2.hasScope('delete')")
//启动类上添加注解 prePostEnabled即在请求方法之前执行校验
@EnableGlobalMethodSecurity(prePostEnabled = true)
4.针对具体用户
//@see org.springframework.security.core.userdetails
//添加访问方法的控制具体用户注解 hasRole权限有哪些 是通过UserDetailsService中的loadUserByUsername方法来加载
@PreAuthorize("hasRole('ROLE_ADMIN')")
//启动类上添加注解 prePostEnabled即在请求方法之前执行校验
@EnableGlobalMethodSecurity(prePostEnabled = true)
5.如果角色和权限 实时在变化 以上方式就不太合适 假设我现在有一个权限控制系统来获取用户拥有的角色或者权限 那么怎么在网关上来集成并实现呢
//修改网关配置 指定访问规则表达式 permissionService.hasPermission(request,authentication)
@Configuration
@EnableResourceServer
public class ZuulSecurityConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("gateway");
}
/**
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/token/**").permitAll()
.anyRequest().access("#permissionService.hasPermission(request,authentication)"); //指定访问规则 authentication当前用户
}
}
这样写是不会生效的 我们需要自己来实现 PermissionService
import org.springframework.security.core.Authentication;
import javax.servlet.http.HttpServletRequest;
/**
* @author Jax
* @Description 权限控制接口
* @Date 2020/6/27 13:11
*/
public interface PermissionService {
boolean hasPermission(HttpServletRequest request, Authentication auth);
}
实现这个方法
import cn.hutool.core.util.RandomUtil;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
/**
* @author Jax
* @Description 权限控制实现类
* @Date 2020/6/27 13:13
*/
@Service
public class PermissionServiceImpl implements PermissionService {
@Override
public boolean hasPermission(HttpServletRequest request, Authentication auth) {
//TODO 需要查询业务逻辑 来获取用户的权限信息 这里采用 反射方法 打印
System.err.println(request.getRequestURI());
System.err.println(ReflectionToStringBuilder.toString(auth));
return RandomUtil.randomInt() % 2 == 0;
}
}
然后重写权限表达式解析方法
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.web.FilterInvocation;
import org.springframework.stereotype.Component;
/**
* @author Jax
* @Description 权限表达式解析处理器
* @Date 2020/6/27 13:23
*/
@Component
public class ZuulWebSecurityExpressionHandler extends OAuth2WebSecurityExpressionHandler {
//注入我们自己的permissionService
@Autowired
private PermissionService permissionService;
/**
* 创建一个评估的上下文
* @param authentication
* @param invocation
* @return
*/
@Override
protected StandardEvaluationContext createEvaluationContextInternal(Authentication authentication, FilterInvocation invocation) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(authentication, invocation);
standardEvaluationContext.setVariable("permissionService",permissionService);
return standardEvaluationContext;
}
}
最后一步 在第5步开始的 ZuulSecurityConfig添加配置
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
/**
* @Description 网关资源认证配置
* @Date 2020/6/24 16:43
* @Author Jax
*/
@Configuration
@EnableResourceServer
public class ZuulSecurityConfig extends ResourceServerConfigurerAdapter {
//注入我们自己的权限表达式解析处理器
@Autowired
private ZuulWebSecurityExpressionHandler securityExpressionHandler;
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId("gateway");
resources.expressionHandler(securityExpressionHandler);
}
/**
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().access("#permissionService.hasPermission(request,authentication)"); //指定访问规则 authentication当前用户
}
}
现在所有的权限 都是在网关来控制 可能会出现越权的情况 可以在每一个微服务都去做权限的校验(即上述方法 不推荐这样去做),一般细粒度的权限90%以上都是在网关上来控制 微服务之间互相调的时候 可以使用ip的黑白名单来控制访问的权限 !
此博客纪录为自己项目中需要使用的技术栈而学习的笔记~~~