SpringCloud+OAuth2+Spring Security+Redis+Jwt+Gateway/zuul实现的微服务统一认证授权

转载请表明出处 https://blog.csdn.net/Amor_Leo/article/details/101751690 谢谢

SpringCloud+OAuth2+Spring Security+Redis+Jwt+Gateway/zuul实现的微服务统一认证授权

  • 源码
  • OAuth 2.0 简单介绍
    • 角色
    • 客户端的授权模式
    • AuthorizationServer 授权服务接口介绍
  • JWT 简介
    • 规范
    • 构成
  • 搭建
    • zuul网关
    • 认证服务
    • 资源服务
    • 测试

源码

代码太多了,贴的不完整 ,可以看我上传到github里的☞☞ 源码
推荐看V3的
搭建

OAuth 2.0 简单介绍

理解Oauth2可以看 阮一峰 理解Oauth

角色

先区分下OAuth 2.0 中有哪些角色,阮一峰博客里写的更精确:
Client: 客户端,也就是Third-party application - 第三方应用程序
Service:服务端,也就是服务的提供者
User: 用户,也就是Resource Owner - 资源所有者
User Agent:用户代理,如浏览器,下文中将其与Client合并考虑。
Authorization Server:认证服务器,即服务提供商专门用来处理认证的服务器。
Resource Server:资源服务器,即服务提供商存放用户生成的资源的服务器。

客户端的授权模式

客户端必
须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。
授权码模式(authorization code)
简化模式(implicit)
密码模式(resource owner password credentials)
客户端模式(client credentials)

AuthorizationServer 授权服务接口介绍

/oauth/authorize:验证接口, AuthorizationEndpoint
/oauth/token:获取token
/oauth/confirm_access:用户授权
/oauth/error:认证失败
/oauth/check_token:资源服务器用来校验token
/oauth/token_key:jwt模式下获取公钥;位于:TokenKeyEndpoint ,通过 JwtAccessTokenConverter 访问key

JWT 简介

规范

JWT – Json Web Token, 如其名,使用Json方式保存Web Token的协议。网上有各种解读,个人理解,这就是一个 客户端Session - Session保存在客户端,而不是通常的保存在服务端。

构成

JWT三部分组成:
Header 头部:JSON方式描述JWT基本信息,如类型和签名算法。使用Base64编码为字符串
Payload 载荷:JSON方式描述JWT信息,除了标准定义的,还可以添加自定义的信息。同样使用Base64编码为字符串。
iss: 签发者
sub: 用户
aud: 接收方
exp(expires): unix时间戳描述的过期时间
iat(issued at): unix时间戳描述的签发时间
Signature 签名:将前两个字符串用 . 连接后,使用头部定义的加密算法,利用密钥进行签名,并将签名信息附在最后。

搭建

eureka Server就不写了,可以看我另一个博客 SpringCloud Eureka
代码没有贴完整,只贴了主要的,感兴趣的可以去看我github上的 源码

zuul网关

pom

		
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-zuul
        
        
        
            org.springframework.cloud
            spring-cloud-starter-oauth2
        

yml

zuul:
  host:
    connect-timeout-millis: 20000
    socket-timeout-millis: 20000
  sensitive-headers:  Cookie,Set-Cookie
  routes:
    auth:
      path: /auth/**
      service-id: sophia-auth
    admin:
      path: /admin/**
      service-id: sophia-admin
security:
  oauth2:
    client:
      access-token-uri: http://localhost:${server.port}/auth/oauth/token
      user-authorization-uri: http://localhost:${server.port}/auth/oauth/authorize
      client-id: sophia-admin
      client-secret: sophia-admin-secret
    resource:
      user-info-uri: http://localhost:${server.port}/auth/home/principal
      prefer-token-info: false
      jwt:
        key-value: sophia_oauth_key
#        key-uri: http://localhost:${server.port}/auth/oauth/token_key
# key-value跟key-uri:token的验证可以直接在本地(即本服务)完成,不需要连接wsm-oauth服务认证服务器。
#              注意:如果要使用key-value对称加密方式,
#                    到wsm-oauth服务AuthorizationServerConfig类JwtAccessTokenConverter方法中使用对称加密方式
#    jwt:
#      key-value: sophia #对称加密方式
#      key-uri: http://localhost:${server.port}/auth/oauth/token_key #非对称加密方式 (获取公钥)

ribbon:
  ReadTimeout: 10000 #请求处理的超时时间
  ConnectTimeout: 5000 #请求连接的超时时间
  MaxAutoRetries: 1 #对当前实例的重试次数
  MaxAutoRetriesNextServer: 2 #切换实例的重试次数
  eureka:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 100000

启动类

@EnableZuulProxy
@EnableDiscoveryClient
@EnableOAuth2Sso
@SpringBootApplication

去掉security的登录验证

@Configuration
@EnableOAuth2Sso
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
    }
}

认证服务

auth pom

     
        
            com.scaffolding.sophia
            sophia_common_config
            ${sophia-common.version}
        
        
        
        
            com.scaffolding.sophia
            sophia_common_security
            ${sophia-common.version}
        
        
        
            org.springframework.boot
            spring-boot-starter-actuator
        
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        

yml

sophia:
  security:
    oauth2:
      clients[0]:
        client_id: sophia-admin
        client_secret: sophia-admin-secret
        accessTokenValidatySeconds: 21600
        refreshTokenValiditySeconds: 28800
      clients[1]:
        client_id: amor-admin
        client_secret: amor-admin-secret
        accessTokenValidatySeconds: 21600
        refreshTokenValiditySeconds: 28800
    web:
      loginPage: /index.html
      unInterceptUris: /index.html,/static/**,/authentication/form,/authentication/require,/oauth/**,/actuator/**,/druid/*

# 集成了资源服务
# 直接放行URL
ignore:
  urls:
    - /index.html
    - /static/**
    - /authentication/form
    - /authentication/require
    - /oauth/**
    - /actuator/**
    - /druid/*

ribbon:
  ReadTimeout: 10000 #请求处理的超时时间
  ConnectTimeout: 5000 #请求连接的超时时间
  MaxAutoRetries: 1 #对当前实例的重试次数
  MaxAutoRetriesNextServer: 2 #切换实例的重试次数
  eureka:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000

#熔断器开启
feign:
  hystrix:
    enabled: true
  okhttp:
    enabled: true
  httpclient:
    enabled: false

认证配置

/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.auth.config
 * @ClassName: SophiaAuthorizationServerConfig
 * @Description: 认证服务
 * @Version: 1.0
 */
@Configuration
@EnableAuthorizationServer
public class SophiaAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private SophiaUserDetailService sophiaUserDetailService;
    @Autowired
    private SophiaSecurityProperties securityProperties;
    @Autowired
    private RedisConnectionFactory connectionFactory;
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private  PasswordEncoder passwordEncoder;

    /**
     * 配置客户端详情信息,客户端详情信息在这里进行初始化,通过数据库来存储调取详情信息
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        if (ArrayUtils.isNotEmpty(securityProperties.getOauth2().getClients())) {
            for (OAuth2ClientProperties client : securityProperties.getOauth2().getClients()) {
                builder
                        .withClient(client.getClientId())
                        .secret(new BCryptPasswordEncoder().encode(client.getClientSecret()))
                        // .resourceIds("admin","auth")
                        //设置token的有效期,不设置默认12小时
                        .accessTokenValiditySeconds(client.getAccessTokenValidatySeconds())
                        //设置刷新token的有效期,不设置默认30天
                        .refreshTokenValiditySeconds(client.getRefreshTokenValiditySeconds())
                        .redirectUris("http://www.baidu.com")
                        .authorizedGrantTypes("authorization_code","client_credentials", "refresh_token", "password")
                        .scopes("all", "read", "write")
                        .autoApprove(true);
            }
        }
    }

    /**
     * 配置授权服务器端点
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        // 自定义jwt生成token方式
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), accessTokenConverter()));
        //指定认证管理器
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(sophiaUserDetailService)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                //指定token存储位置
                .tokenStore(tokenStore())
                .accessTokenConverter(accessTokenConverter())
                // 自定义jwt生成token方式
                .tokenEnhancer(tokenEnhancerChain)
                // 配置TokenServices参数 如果需要jw的token而不是默认的uuid 那把他注释
                //.tokenServices(defaultTokenServices())
                .tokenServices(defaultTokenServices())
                .reuseRefreshTokens(false)
        // ;  //自定义异常处理
                .exceptionTranslator(new SophiaWebResponseExceptionTranslator());
    }


    /**
     * 注入自定义token生成方式(jwt)
     */
    @Bean
    public TokenEnhancer tokenEnhancer() {
        return new JwtTokenEnhancer();
    }

    /**
     * 注意,自定义TokenServices的时候,需要设置@Primary,否则报错
     */
    @Primary
    @Bean
    public DefaultTokenServices defaultTokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());
        tokenServices.setSupportRefreshToken(true);
        // 这里如果设置为false则不能更新refresh_token,如果需要刷新token的功能需要设置成true
        tokenServices.setSupportRefreshToken(true);
        // 设置上次RefreshToken是否还可以使用 默认为true
        tokenServices.setReuseRefreshToken(false);
        // token有效期自定义设置,默认12小时
        tokenServices.setAccessTokenValiditySeconds(60 * 60 * 6);
        // refresh_token默认30天
        tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 8);
        tokenServices.setTokenEnhancer(tokenEnhancer());
        return tokenServices;
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
        oauthServer
                // 开启/oauth/token_key验证端口无权限访问
                .tokenKeyAccess("permitAll()")
                // 开启/oauth/check_token验证端口认证权限访问
                .checkTokenAccess("isAuthenticated()")
                .passwordEncoder(passwordEncoder)
                //允许表单认证
                .allowFormAuthenticationForClients();
    }

    /**
     * 对Jwt签名时,增加一个密钥
     * JwtAccessTokenConverter:对Jwt来进行编码以及解码的类
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter();
        //测试用,资源服务使用相同的字符达到一个对称加密的效果,生产时候使用RSA非对称加密方式
        accessTokenConverter.setSigningKey(GlobalsConstants.OAUTH_SIGNING_KEY);
        return accessTokenConverter;
    }

    /**
     * token store
     */
    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore(connectionFactory);
    }

}

security 配置

/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.auth.config
 * @ClassName: SophiaWebSecurityConfig
 * @Description: web security 访问安全配置
 * @Version: 1.0
 */
@EnableWebSecurity
@Configuration
@AutoConfigureBefore({SophiaResourceServerConfig.class, SophiaAuthorizationServerConfig.class})
public class SophiaWebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SophiaUserDetailService sophiaUserDetailService;
    @Autowired
   private SophiaSecurityProperties securityProperties;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.requestMatchers()
                .anyRequest()
                .and()
                .authorizeRequests()
                .antMatchers(securityProperties.getWeb().getUnInterceptUris())
                .permitAll()
                .and()
                .authorizeRequests().anyRequest().authenticated()
                .and()
                .csrf()
                .disable();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(sophiaUserDetailService).passwordEncoder(new BCryptPasswordEncoder());
    }

}

资源服务

资源服务是写成公共的 认证服务和用户服务以及其他服务需要集成它

资源服务配置


/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.common.security.config
 * @ClassName: SophiaResourceServerConfig
 * @Description: 资源服务 资源访问权限配置: 给接口地址让security管理起来,如哪些不需要授权能访问;哪些需要登录授权后能访问,哪些需要用户拥有这些角色才能访问。
 * 优先级低于AuthorizationServerConfigurerAdapter
 * @Version: 1.0
 */
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan("com.scaffolding.sophia.common.security")
public class SophiaResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Autowired
    private FilterIgnoreProperties ignorePropertiesConfig;

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    /**
     * 对Jwt签名时,增加一个密钥
     * JwtAccessTokenConverter:对Jwt来进行编码以及解码的类
     */
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
        //对称加密方式 测试用,资源服务使用相同的字符达到一个对称加密的效果,生产时候使用RSA非对称加密方式
        jwtAccessTokenConverter.setSigningKey(GlobalsConstants.OAUTH_SIGNING_KEY);
        return jwtAccessTokenConverter;
    }

    /**
     * token store
     */
    @Bean
    public TokenStore tokenStore() {
        return new RedisTokenStore(redisConnectionFactory);
    }

    @Override
    @SneakyThrows
    public void configure(HttpSecurity httpSecurity) {
        //允许使用iframe 嵌套,避免swagger-ui 不被加载的问题
        httpSecurity.headers().frameOptions().disable();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>
                .ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //对配置的url放行 不进行验证
        ignorePropertiesConfig.getUrls()
                .forEach(url -> registry.antMatchers(url).permitAll());
        registry.anyRequest().authenticated()
                .and().csrf().disable();
    }


    @Override
    @CrossOrigin
    public void configure(ResourceServerSecurityConfigurer resources) {
    	resources
                .tokenStore(tokenStore())
                //自定义Token异常信息,用于token校验失败返回信息
                .authenticationEntryPoint(new MyAuthExceptionEntryPoint())
                //授权异常处理
                .accessDeniedHandler(new MyAccessDeniedHandler());
    }

}

自定义生成jwt


/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.common.security.config
 * @ClassName: JwtTokenEnhancer
 * @Description: 自定义token生成携带的信息
 * @Version: 1.0
 */
@Component
public class JwtTokenEnhancer implements TokenEnhancer {

    // private DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
        final Map<String, Object> additionalInfo = new HashMap<>();
        // 给/oauth/token接口加属性roles,author
        JSONObject jsonObject = new JSONObject(authentication.getPrincipal());
        List<Object> authorities = jsonObject.getJSONArray("authorities").toList();
        StringBuilder stringBuilder = new StringBuilder();
        for (Object authority : authorities) {
            Map map = (Map) authority;
            stringBuilder.append(map.get("authority"));
            stringBuilder.append(",");
        }
        String roles = stringBuilder.toString();
        additionalInfo.put("roles", roles.substring(0, roles.length() - 1));
        additionalInfo.put("author", "sophia");
        // additionalInfo.put("createTime", df.format(LocalDateTime.now()));
        ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
        return accessToken;
    }
}

UserDetailsService


/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.common.security.service
 * @ClassName: SophiaUserDetailService
 * @Description: 用户登录 查询登录用户 使用feign调用用户服务暴露的接口  这里面的两个接口需要过滤 不验证
 * @Version: 1.0
 */
@Component
public class SophiaUserDetailService implements UserDetailsService {

    @Autowired
    private UserClient userClient;
    @Autowired
    private AuthorityClient authorityClient;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if(StringUtils.isEmpty(username)){
            throw new CommonException("登录名不能为空");
        }
        ApiResponse apiResponse = userClient.getUserByUserName(username);
        User user = JSON.parseObject(JSON.toJSONString( apiResponse.getData(), true),User.class);
        if (user == null) {
            throw new CommonException("登录名不存在");
        } else if (BizConstants.USER_STATUS_EXPIRED.equals(user.getStatus())) {
            throw new CommonException("用户已过期");
        } else if (BizConstants.USER_STATUS_LOCKED.equals(user.getStatus())) {
            throw new CommonException("用户已锁定");
        } else if (BizConstants.USER_STATUS_UNUSED.equals(user.getStatus())) {
            throw new CommonException("用户已禁用");
        }
        ApiResponse response = authorityClient.getAuthorityByUserId(user.getId());
        List<Authority> authList = JSON.parseArray(JSON.toJSONString(response.getData(), true),Authority.class);
        List<GrantedAuthority> lists = new ArrayList<>();
        if(authList != null && authList.size()>0){
            for (Authority auth : authList) {
                lists.add(new SimpleGrantedAuthority(auth.getAuthCode()));
            }
        }
        //数据库密码是加密的
        LoginUser loginUser = new LoginUser(username,user.getPassword(),user.getNickname(),user.getStatus(), lists);
        // LoginUser loginUser = new LoginUser(username,passwordEncoder.encode(user.getPassword()),user.getNickname(),user.getStatus(), lists);
        loginUser.setId(user.getId());
        loginUser.setDeptId(user.getDeptId());
        return loginUser;
    }
}

因为查询用户信息使用了feign,会有一些坑,比如token没有传递下来,token失效等等
feign配置

/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.common.feign
 * @ClassName: FeignRequestInterceptorConfig
 * @Description:  
 * @Version: 1.0
 */
@Component
public class FeignRequestInterceptorConfig  implements RequestInterceptor {

    private  static  final Logger log = LoggerFactory.getLogger(FeignRequestInterceptorConfig.class);

    private final String AUTHORIZATION_HEADER = "Authorization";

    @Override
    public void apply(RequestTemplate requestTemplate) {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();

        if (request != null) {
            log.error("调用feign传递header携带token");

//        只携带token
//            String authorization = request.getHeader(AUTHORIZATION_HEADER);
//            requestTemplate.header("Authorization", authorization);
//             System.err.println("Authorization :\t\t"+ authorization);

//        携带全部
            Enumeration<String> headerNames = request.getHeaderNames();
            if (headerNames != null) {
                while (headerNames.hasMoreElements()) {
                    String name = headerNames.nextElement();
                    String values = request.getHeader(name);
                    requestTemplate.header(name, values);
                    log.debug("name :\t\t" + name);
                    log.debug("values : \t\t" + values);

                }
            }
        }
    }
}
/**
 * @author: LHL
 * @ProjectName: sophia_scaffolding
 * @Package: com.scaffolding.sophia.common.security.config
 * @ClassName: FeignHystrixConcurrencyStrategy
 * @Description: 自定义Feign的隔离策略:
 * 在转发Feign的请求头的时候, 如果开启了Hystrix,
 *    Hystrix的默认隔离策略是Thread(线程隔离策略), 因此转发拦截器内是无法获取到请求的请求头信息的,
 *    可以修改默认隔离策略为信号量模式:hystrix.command.default.execution.isolation.strategy=SEMAPHORE,
 *    这样的话转发线程和请求线程实际上是一个线程, 这并不是最好的解决方法, 信号量模式也不是官方最为推荐的隔离策略;
 * 另一个解决方法就是自定义Hystrix的隔离策略:
 *    思路是将现有的并发策略作为新并发策略的成员变量,在新并发策略中,
 *    返回现有并发策略的线程池、Queue;将策略加到Spring容器即可;
 * @Version: 1.0
 */
@Configuration
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

    private  static  final  Logger log = LoggerFactory.getLogger(FeignHystrixConcurrencyStrategy.class);

    private HystrixConcurrencyStrategy delegate;

    public FeignHystrixConcurrencyStrategy() {
        try {
            this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
            if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
                // Welcome to singleton hell...
                return;
            }

            HystrixCommandExecutionHook commandExecutionHook =
                    HystrixPlugins.getInstance().getCommandExecutionHook();

            HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
            HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
            HystrixPropertiesStrategy propertiesStrategy =
                    HystrixPlugins.getInstance().getPropertiesStrategy();
            this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);

            HystrixPlugins.reset();
            HystrixPlugins instance = HystrixPlugins.getInstance();
            instance.registerConcurrencyStrategy(this);
            instance.registerCommandExecutionHook(commandExecutionHook);
            instance.registerEventNotifier(eventNotifier);
            instance.registerMetricsPublisher(metricsPublisher);
            instance.registerPropertiesStrategy(propertiesStrategy);
        } catch (Exception e) {
            log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
        }
    }

    private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
                                                 HystrixMetricsPublisher metricsPublisher,
                                                 HystrixPropertiesStrategy propertiesStrategy) {
        if (log.isDebugEnabled()) {
            log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
                    + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
                    + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
            log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
        }
    }

    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return new WrappedCallable<>(callable, requestAttributes);
    }

    @Override
    public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                            HystrixProperty<Integer> corePoolSize,
                                            HystrixProperty<Integer> maximumPoolSize,
                                            HystrixProperty<Integer> keepAliveTime,
                                            TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,
                unit, workQueue);
    }

    @Override
    public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,
                                            HystrixThreadPoolProperties threadPoolProperties) {
        return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);
    }

    @Override
    public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {
        return this.delegate.getBlockingQueue(maxQueueSize);
    }

    @Override
    public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {
        return this.delegate.getRequestVariable(rv);
    }

    static class WrappedCallable<T> implements Callable<T> {
        private final Callable<T> target;
        private final RequestAttributes requestAttributes;

        public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {
            this.target = target;
            this.requestAttributes = requestAttributes;
        }

        @Override
        public T call() throws Exception {
            try {
                RequestContextHolder.setRequestAttributes(requestAttributes);
                return target.call();
            } finally {
                RequestContextHolder.resetRequestAttributes();
            }
        }
    }
}

用户微服务集成了资源服务
yml

security:
  oauth2:
    client:
      client-id: sophia-admin
      client-secret: sophia-admin-secret
    resource:
      jwt:
        key-value: sophia_oauth_key

ribbon:
  ReadTimeout: 10000 #请求处理的超时时间
  ConnectTimeout: 5000 #请求连接的超时时间
  MaxAutoRetries: 1 #对当前实例的重试次数
  MaxAutoRetriesNextServer: 2 #切换实例的重试次数
  eureka:
    enabled: true

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 60000

#熔断器开启
feign:
  hystrix:
    enabled: true
  okhttp:
    enabled: true
  httpclient:
    enabled: false

# 直接放行URL
ignore:
  urls:
    - /actuator/**
    - /user/api/**
    - /authority/api/**

资源服务中调用了用户服务中的api

@FeignClient(contextId = "userClient", name = ServiceNameConstants.SOPHIA_ADMIN, configuration = FeignRequestInterceptorConfig.class, fallback = UserClientFallBack.class)

测试

使用postman
http://localhost:8080/auth/oauth/token?username=admin&password=123456&grant_type=password&client_id=sophia-admin&client_secret=sophia-admin-secret&scope=all
获取token
SpringCloud+OAuth2+Spring Security+Redis+Jwt+Gateway/zuul实现的微服务统一认证授权_第1张图片

请求
设置token
Bearer Token
SpringCloud+OAuth2+Spring Security+Redis+Jwt+Gateway/zuul实现的微服务统一认证授权_第2张图片

整合Swaggergateway整合swagger

V1的sql已改 (是我忘记改git的sql了)

你可能感兴趣的:(微服务)