网关有以下几个作用:
- 统一入口:未全部为服务提供一个唯一的入口,网关起到外部和内部隔离的作用,保障了后台服务的安全性。
- 鉴权校验:识别每个请求的权限,拒绝不符合要求的请求。
- 动态路由:动态的将请求路由到不同的后端集群中。
- 减少客户端与服务端的耦合:服务可以独立发展,通过网关层来做映射。
在oauth2网关除了以上作用 还有鉴权的作用 如果token无效 则直接返回
GetwayApplication
@EnableZuulProxy说明为zuul网关服务
restTemplate配置开启服务间调用
@EnableEurekaClient //标识是eureka客户端
@SpringBootApplication
@EnableZuulProxy
public class GetwayApplication {
public static void main(String[] args) {
SpringApplication.run(GetwayApplication.class, args);
}
@Bean
@LoadBalanced
public RestTemplate restTemplate(RestTemplateBuilder builder){
return builder.build();
}
}
GatewayConfig
解决跨域问题
@Configuration
public class GatewayConfig {
/**
* 配置全局解决cors跨域问题
*
* @return
*/
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.setAllowCredentials(true);
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedOrigin("*");
corsConfiguration.addAllowedMethod("*");
// ↓核心代码
corsConfiguration.addExposedHeader("Authorization");
corsConfiguration.addExposedHeader("WWW-Authenticate");
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
SpringSecurityConfig
Spring security核心配置 这里配置为开放所有权限
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 当前将所有请求放行,交给资源配置类进行资源权限判断
* 因为默认情况下会拦截所有请求
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().permitAll();
}
}
TokenConfig
token的保存方式 不多说
@Configuration
public class TokenConfig {
/**
* 使用redis存储
*/
@Autowired
private RedisConnectionFactory redisConnectionFactory;
@Bean
public TokenStore tokenStore() {
return new RedisTokenStore(redisConnectionFactory);
}
}
ResourceServerConfig
资源服务器的核心配置 说白了网关也作为一个资源提供给第三方访问 也就是说 在访问时会判断token是否有权限访问 getway资源 所以在 oauth_client_details表resource_ids字段中 应该有以下配置 对应resources.resourceId(RESOURCE_ID)
@Configuration
// 标识为资源服务器,请求服务中的资源,就要带着token过来,找不到token或token是无效访问不了资源
@EnableResourceServer
// 开启方法级别权限控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig {
public static final String RESOURCE_ID = "getway";
/**
* 异常返回json
*/
@Autowired
private AuthExceptionEntryPointConfig authExceptionEntryPointConfig;
/**
* 上文有不多说
*/
@Autowired
private TokenStore tokenStore;
/**
* 认证服务资源
*/
@Configuration
@EnableResourceServer
public class AuthResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(RESOURCE_ID) //验证是否有getway权限 在oauth_client_details中配置
.tokenStore(tokenStore)//验证token保存方式与验证方式
.authenticationEntryPoint(authExceptionEntryPointConfig) //token验证失败返回json
.stateless(true);// 会话机制stateless开启
}
@Override
public void configure(HttpSecurity http) throws Exception {
// 关于请求认证服务器资源,则所有请求放行
http.authorizeRequests().anyRequest().permitAll();
}
//加密密码 不多说
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
}
application.yml
这时候小伙伴问了 那么怎么验证token是否有效的呢
在认证中心中提供一个接口/oauth/check_token 在认证服务器中AuthorizationServerConfig配置的就是他!!!!
扫描二维码关注公众号,回复:
12471626 查看本文章

这个接口地址等的配置 在application.yml中
同时填写接入的第三方客户端的信息(这个填写访问user资源的前端信息即可) 对应着oauth_client_details表的client_id与client_secret字段 (记住是解密后的密码)
这样就可以调用了/oauth/check_token接口了
同时zuul的分发关系也在其中配置
server:
port: 6001 # 端口号
spring:
application:
name: gateway
eureka:
instance:
hostname: ${spring.cloud.client.ip-address}
prefer-ip-address: true
instance-id: ${eureka.instance.hostname}:${server.port}
client:
security:
basic:
user: admin
password: xxxxx
service-url:
defaultZone: http://${eureka.client.security.basic.user}:${eureka.client.security.basic.password}@localhost:5001/eureka
zuul: # 网关配置
host:
connect-timeout-millis: 15000 #HTTP连接超时要比Hystrix的大
socket-timeout-millis: 60000 #socket超时
sensitive-headers: null # 默认Zuul认为请求头中 "Cookie", "Set-Cookie", "Authorization" 是敏感信息,它不会转发请求,因为把它设置为空,就会转发了
add-host-header: true # 正确的处理重定向操作
routes:
authentication: # 路由名称,名称任意,保持所有路由名称唯一
path: /user/** # 访问路径,转发到 auth-server 服务处理
serviceId: user-server # 指定服务ID,会自动从Eureka中找到此服务的ip和端口
stripPrefix: false # 代理转发时去掉前缀,false:代理转发时不去掉前缀 例如:为true时请求 /product/get/1,代理转发到/get/1
ribbon:
ConnectTimeout: 10000 # 连接超时时间(ms)
ReadTimeout: 10000 # 通信超时时间(ms)
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 60000 # 设置hystrix的超时时间为6000ms
#验证token是否正确
security:
oauth2:
client:
client-id: user-vue
client-secret: 1234
resource:
token-info-uri: http://localhost:8001/oauth/check_token
#前段访问地址
cors:
ip: http://192.168.xx.xx:8080
AuthExceptionEntryPointConfig
如果token无效 返回给前台json
这里楼主有个问题 在测试跨域时 需要指定前端ip 否则还是无效 corsIp为前端ip地址
@Component
public class AuthExceptionEntryPointConfig implements AuthenticationEntryPoint {
/**
* 跨域前段ip
*/
@Value("${cors.ip}")
private String corsIp;
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws ServletException {
Throwable cause = authException.getCause();
response.setStatus(HttpStatus.OK.value());
response.addHeader("Access-Control-Allow-Origin", corsIp);
response.addHeader("Access-Control-Allow-Methods", "POST,OPTIONS,PUT,HEAD");
response.addHeader("Access-Control-Max-Age", "3600000");
response.addHeader("Access-Control-Allow-Credentials", "true");
response.addHeader("Access-Control-Allow-Headers",
"Authentication,Origin, X-Requested-With, Content-Type, Accept,token");
response.setHeader("Content-Type", "application/json;charset=UTF-8");
Result result;
try {
if (cause instanceof InvalidTokenException) {
result = new Result(402, "认证失败,无效或过期token");
} else {
result = new Result(401, "认证失败,没有携带token");
}
response.getWriter().write(new ObjectMapper().writeValueAsString(result));
} catch (IOException e) {
e.printStackTrace();
}
}
}
AuthenticationFilter
将token解析后放入请求头 转发给需要调用的资源服务器
@Component
public class AuthenticationFilter extends ZuulFilter {
Logger logger = LoggerFactory.getLogger(getClass());
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 如果解析到令牌就会封装到OAuth2Authentication对象
if (!(authentication instanceof OAuth2Authentication)) {
return null;
}
logger.info("网关获取到认证对象:" + authentication);
// 用户名,没有其他用户信息
Object principal = authentication.getPrincipal();
// 获取用户所拥有的权限
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
Set<String> authoritySet = AuthorityUtils.authorityListToSet(authorities);
// 请求详情
Object details = authentication.getDetails();
Map<String, Object> result = new HashMap<>(3);
result.put("principal", principal);
result.put("authorities", authoritySet);
result.put("details", details);
// 获取当前请求上下文
RequestContext context = RequestContext.getCurrentContext();
// 将用户信息和权限信息转成json,再通过base64进行编码
String base64 = Base64Utils.encodeToString(JSON.toJSONString(result).getBytes());
// 添加到请求头
context.addZuulRequestHeader("authentication-token", base64);
return null;
}
}