SpringCloud基础权限框架搭建(2)

该篇用以记录我在整合springcloud框架中遇到林林总总的问题,因为目前对于微服务框架的理解还很有局限性,下文有误的地方欢迎批评指正

先说明一下思路,auth服务用于处理授权请求,user服务作为资源服务器同时也提供注册登录的开放接口(采用密码模式来获取令牌),zuul服务除了作为服务网关也是受auth服务保护的资源服务器,开放对auth与user的请求,HystrixDashboard/Turbine用于监控熔断

1)Spring boot - 微服务的入门级微框架,用来简化 Spring 应用的初始搭建以及开发过程。
2)Eureka - 云端服务发现,一个基于 REST 的服务,用于定位服务,以实现云端中间层服务发现和故障转移。
3)Hystrix - 熔断器,容错管理工具,旨在通过熔断机制控制服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。
4)Hystrix-dashboard - 一款针对Hystrix进行实时监控的工具,可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据
5)Turbine - Turbine 是聚合服务器发送事件流数据的一个工具,用来监控集群下 hystrix 的 metrics 情况。
6)Zuul - Zuul是在云平台上提供动态路由,监控,弹性,安全等边缘服务的框架。Zuul 相当于是设备和 Netflix 流应用的 Web网站后端所有请求的前门。
7)Ribbon - 提供云端负载均衡,有多种负载均衡策略可供选择,可配合服务发现和断路器使用。
8)Feign - Feign是一种声明式、模板化的 HTTP 客户端。
9)RestTemplate - RestTemplate是一种支持Rest风格架构的互联网的请求方式模板类。
10)JWT - JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。
11)Spring Cloud OAuth2 - 基于 Spring Security 和 OAuth2的安全工具包,为你的应用程序添加安全控制。

项目结构:

  1. sc-eurekaserver:注册中心
  2. common:公共包,方便其他服务引用
  3. sc-auth-server:授权服务器
  4. sc-zuul-server:服务网关
  5. sc-user-server:用户服务
  6. sc-hystrix-dashboard:熔断监控

以下仅贴出部分核心代码,详情见源码
源码路径:
传送门

文章目录

  • parent
  • 注册中心sc-eurekaserver
  • 公共包common
  • 授权服务器sc-auth-server
  • 服务网关sc-zuul-server
  • 用户服务sc-user-server
  • 熔断监控sc-hystrix-dashboard
  • 补充:使用Feign/RestTemplate调用服务时的权鉴处理与服务调用案例
    • 权鉴处理
    • feign
    • RestTemplate

parent

pom.xml

    <modules><module>cloud1.0-common</module><module>cloud1.0-eureka-server</module><module>cloud1.0-auth-server</module><module>cloud1.0-zuul-server</module><module>cloud1.0-user-server</module><module>cloud1.0-hystrix-dashboard</module></modules><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><java.version>1.8</java.version><spring-cloud.version>Finchley.RELEASE</spring-cloud.version><common.version>0.0.1-SNAPSHOT</common.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId></dependency></dependencies>

注册中心sc-eurekaserver

与普通的Eureka工程无太大差异

pom.xml

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId></dependency>
</dependencies>

bootstrap.yml

server:port: 8761
spring:application:name: sc-eurekaserver
eureka:client:registerWithEureka: falsefetchRegistry: false

EurekaServerApplication.java

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerApplication.class, args);}
}

公共包common

作为公共包被其他服务所引用

pom.xml

    <dependencies><!-- RestTemplate和FeignClient --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.31</version></dependency></dependencies>

src/main/resource/META-INF/spring.factories(该文件是为了将公共的bean注册到spring容器)

这里解释一下,由于SpringBoot默认包扫描机制是从启动类所在包开始,扫描当前包及其子包下的所有文件,所以配置在公共包里面定义的bean在引用工程将不会注册,所以在这里将公共的bean配置在spring.factories。当然,也可以在引用工程使用@ComponentScan注解来指定扫描包

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\cn.springcloud.book.common.config.CommonConfiguration

CommonConfiguration.java
用于定义全局的Feign与RestTemplate拦截器

在springcloud项目中,服务消费者与服务生产者一般通过Feign与RestTemplate来实现服务调用。Feign默认实现负载均衡,RestTemplate在这里添加负载均衡配置

@SuppressWarnings("deprecation")
@Configuration
@EnableWebMvc
public class CommonConfiguration extends WebMvcConfigurerAdapter {/*** 创建Feign请求拦截器*/@Bean@ConditionalOnClass(Feign.class)public FeignUserContextInterceptor feignTokenInterceptor() {return new FeignUserContextInterceptor();}/*** RestTemplate拦截器* @LoadBalanced:启用了负载均衡后url不再写端口而是直接写serviceId,ps:http://sc-auth-server/oauth/token* @return*/@LoadBalanced// 开启客户端负载均衡@Beanpublic RestTemplate restTemplate() {RestTemplate restTemplate = new RestTemplate();restTemplate.getInterceptors().add(new RestTemplateUserContextInterceptor());return restTemplate;}}

授权服务器sc-auth-server

pom.xml

这里对于SecurityOAuth2数据与令牌存储使用mysql数据库
注意需添加免编译过滤jks与cert文件(jwt令牌与公钥),关于Security与OAuth2的使用可以参见另外两篇博客:
https://blog.csdn.net/weixin_37769855/article/details/103066524
https://blog.csdn.net/weixin_37769855/article/details/102840606
关于jwt令牌的生成见:
https://blog.csdn.net/weixin_37769855/article/details/103109518

    <dependencies><dependency><groupId>cn.springcloud.book</groupId><artifactId>cloud1.0-common</artifactId><version>${parent.version}</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><configuration><nonFilteredFileExtensions><nonFilteredFileExtension>cert</nonFilteredFileExtension><nonFilteredFileExtension>jks</nonFilteredFileExtension></nonFilteredFileExtensions></configuration></plugin></plugins></build>

application.yml

spring:application:name: sc-auth-serverdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/oauth2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=falseusername: rootpassword: rootjpa:properties:hibernate:dialect: org.hibernate.dialect.MySQL5Dialecthibernate:ddl-auto: updateshow-sql: trueeureka:client:serviceUrl:defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/instance:prefer-ip-address: true
management:security:enabled: falseendpoints:web:exposure:include: hystrix.stream
server:port: 8078

resources目录

test-jwt.jks和public.cert分别为私钥和公钥,公钥用于授权服务器配置,公钥用于资源服务器配置


启动类AuthServerApplication

@SpringBootApplication
@EnableDiscoveryClient//配置本应用将使用服务注册和服务发现
@EnableCircuitBreaker//启动断路器
public class AuthServerApplication {public static void main(String[] args) {SpringApplication.run(AuthServerApplication.class, args);}}

sql

用户/角色/权限的表结构可以根据自己的业务需求做调整,这里默认添加了一个admin/admin用户
关于entity、repository的代码就不贴出来了

DROP TABLE IF EXISTS `sys_permission`;CREATE TABLE `sys_permission` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT '',`descritpion` varchar(10) DEFAULT '',`url` varchar(10) DEFAULT '',`pid` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;/*Data for the table `sys_permission` */insert  into `sys_permission`(`id`,`name`,`descritpion`,`url`,`pid`) values (3,'ADMIN','','',NULL),(4,'USER','','',NULL);/*Table structure for table `sys_permission_role` */DROP TABLE IF EXISTS `sys_permission_role`;CREATE TABLE `sys_permission_role` (`id` int(11) NOT NULL AUTO_INCREMENT,`role_id` int(11) DEFAULT NULL,`permission_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;/*Data for the table `sys_permission_role` */insert  into `sys_permission_role`(`id`,`role_id`,`permission_id`) values (1,2,4),(2,1,3);/*Table structure for table `sys_role` */DROP TABLE IF EXISTS `sys_role`;CREATE TABLE `sys_role` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT '',PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;/*Data for the table `sys_role` */insert  into `sys_role`(`id`,`name`) values (1,'ADMIN'),(2,'USER');/*Table structure for table `sys_role_user` */DROP TABLE IF EXISTS `sys_role_user`;CREATE TABLE `sys_role_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`Sys_user_id` int(11) DEFAULT NULL,`sys_role_id` int(11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4;/*Data for the table `sys_role_user` */insert  into `sys_role_user`(`id`,`Sys_user_id`,`sys_role_id`) values (1,2,1),(2,3,2);/*Table structure for table `sys_user` */DROP TABLE IF EXISTS `sys_user`;CREATE TABLE `sys_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(10) DEFAULT NULL,`password` varchar(100) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;/*Data for the table `sys_user` admin/admin xuweichao/xuweichao*/insert  into `sys_user`(`id`,`name`,`password`) values (2,'admin','$2a$10$Yks2LoqzBUHEWjyLCnsdtepI4oCNip9yNdf67y19ewF8geORNAO5m'),(3,'xuweichao','$2a$10$kmFQOKZw8l776qXp00Lq9e2drL5MUSpG9YHnQtQwbVzyUjJQwHNha');DROP TABLE IF EXISTS `oauth_access_token`;
CREATE TABLE `oauth_access_token`  (`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`token` blob NULL,`authentication_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`user_name` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`client_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authentication` blob NULL,`refresh_token` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`authentication_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for oauth_client_details
-- ----------------------------
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details`  (`client_id` varchar(250) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,`resource_ids` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`client_secret` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorized_grant_types` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`web_server_redirect_uri` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`authorities` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`access_token_validity` int(11) NULL DEFAULT NULL,`refresh_token_validity` int(11) NULL DEFAULT NULL,`additional_information` varchar(4096) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`autoapprove` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,PRIMARY KEY (`client_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ----------------------------
-- Table structure for oauth_refresh_token
-- ----------------------------
DROP TABLE IF EXISTS `oauth_refresh_token`;
CREATE TABLE `oauth_refresh_token`  (`token_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,`token` blob NULL,`authentication` blob NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

定义权限对象service层接口PermissionService

public interface PermissionService {public List<Permission> findAll();public List<Permission> findByAdminUserId(int userId);
}

PermissionService实现类PermissionServiceImpl

@Service
public class PermissionServiceImpl implements PermissionService {@AutowiredPermissionRepository permissionRepository ;@PersistenceContextprivate EntityManager entityManager;@Overridepublic List<Permission> findAll() {return null;}@Overridepublic List<Permission> findByAdminUserId(int userId) {List<Permission> list = new ArrayList<Permission>();List<Object[]> abcs = entityManager.createNativeQuery("select p.* \n" +"        from Sys_User u\n" +"        LEFT JOIN sys_role_user sru on u.id= sru.Sys_User_id\n" +"        LEFT JOIN Sys_Role r on sru.Sys_Role_id=r.id\n" +"        LEFT JOIN Sys_permission_role spr on spr.role_id=r.id\n" +"        LEFT JOIN Sys_permission p on p.id =spr.permission_id\n" +"        where u.id="+userId).getResultList();for (Object[] abc : abcs) {if(abc[0] != null){Permission permission = new Permission();permission.setId(Integer.valueOf(abc[0]+""));permission.setName((abc[1] != null?abc[1]:"")+"");permission.setDescritpion((abc[2] != null?abc[2]:"")+"");permission.setUrl((abc[3] != null?abc[3]:"")+"");list.add(permission);}}return list;}
}

自定义的UserDetailsService实现类

@Service
public class UserDetailServiceImpl implements UserDetailsService {@AutowiredSysUserRepository sysUserRepository;@AutowiredPermissionService permissionService;@AutowiredPasswordEncoder passwordEncoder;//cn.springcloud.book.auth.config.SecurityConfiguration>BCryptPasswordEncoder@Transactional@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {SysUser sysUser = sysUserRepository.getUserByName(username);List<GrantedAuthority> grantedAuthorities = new ArrayList<>();if (sysUser != null) {System.err.println("sysUser===============" + sysUser);//获取用户的授权List<Permission> permissions = permissionService.findByAdminUserId(sysUser.getId());//声明授权文件for (Permission permission : permissions) {if (permission != null && permission.getName() != null) {GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+permission.getName());//spring Security中权限名称必须满足ROLE_XXXgrantedAuthorities.add(grantedAuthority);}}}System.err.println("grantedAuthorities===============" + grantedAuthorities);return new User(sysUser.getName(), sysUser.getPassword(), grantedAuthorities);}
}

配置WebSecurityConfigurer

注意:WebSecurityConfigurer的过滤顺位要在ResourceServerConfigurer之前,所以在WebSecurityConfigurer和ResourceServerConfigurer添加@Order配置

@Order(2)
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;//注入自定义userdetailservice(com.service.auth.serviceauth.service.impl.UserDetailServiceImpl)@BeanPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();//return PasswordEncoderFactories.createDelegatingPasswordEncoder();//兼容多种密码的加密方式}@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Overrideprotected void configure(HttpSecurity http) throws Exception {//不拦截/oauth/**,/login/**,/logout/**(requestMatchers用于需要过滤多个HttpSecurity的情况)http.requestMatchers().antMatchers("/oauth/**", "/login/**", "/logout/**")//使HttpSecurity接收以"/login/","/oauth/","/logout/"开头请求。.and().authorizeRequests().antMatchers("/oauth/**").authenticated().and().formLogin();}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());//注入自定义的UserDetailsService,采用BCrypt加密}@Override//通过重载该方法,可配置Spring Security的Filter链(HTTP请求安全处理)public void configure(WebSecurity web) throws Exception {super.configure(web);}
}

实现TokenEnhancer接口用于给JWT令牌添加自定义信息

public class CustomTokenEnhancer implements TokenEnhancer {@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken,OAuth2Authentication authentication) {Map<String, Object> additionalInformation = new HashMap<>();String userName = authentication.getUserAuthentication().getName();User user = (User) authentication.getUserAuthentication().getPrincipal();additionalInformation.put("userName", userName);additionalInformation.put("roles", user.getAuthorities());additionalInformation.put("organization",authentication.getName());((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInformation);return accessToken;}
}

配置AuthorizationServerConfigurer

注意:授权服务器(AuthorizationServerConfiguration)的TokenStore与资源服务器(ResourceServerConfig)的TokenStore是不同的,前者直接使用秘钥+密码,后者使用公钥解析

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {@Autowired@Qualifier("authenticationManagerBean")private AuthenticationManager authenticationManager;@Autowired@Qualifier("dataSource")private DataSource dataSource;@Autowiredprivate UserDetailsService userDetailsService;@Autowired@Qualifier("passwordEncoder")PasswordEncoder passwordEncoder;//BCryptPasswordEncoderpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}public JwtAccessTokenConverter jwtAccessTokenConverter() {KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("test-jwt.jks"), "test123".toCharArray());//证书路径和密钥库密码JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setKeyPair(keyStoreKeyFactory.getKeyPair("test-jwt"));//密钥别名return converter;}@Beanpublic TokenEnhancer tokenEnhancer() {return new CustomTokenEnhancer();}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(dataSource);}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));//jwt令牌中添加自定义信息endpoints.tokenStore(tokenStore()).tokenEnhancer(tokenEnhancerChain)//令牌配置.authenticationManager(authenticationManager).userDetailsService(userDetailsService)//必须设置 UserDetailsService 否则刷新token 时会报错.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);endpoints.accessTokenConverter(jwtAccessTokenConverter());//自定义授权页(none)endpoints.pathMapping("/oauth/confirm_access", "/confirm");}@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) throws Exception {security/* 配置token获取合验证时的策略 */.tokenKeyAccess("permitAll()")// 开启/oauth/token_key验证端口无权限访问.checkTokenAccess("isAuthenticated()")// 开启/oauth/check_token验证端口认证权限访问.allowFormAuthenticationForClients();//允许表单登录}
}

1)由于授权服务器本身也提供对外接口所以也配置资源服务ResourceServerConfigurer
2)这里开放/actuator/hystrix.stream是为了后面整合HystrixDashboard/Turbine使用
3)配置资源服务器要注意两点:1需要把accessTokenConverter作为bean注册到容器中;2直接使用converter.setVerifierKey(publicKey)配置公钥时通过网关使用令牌获取资源会提示convert access token to JSON,需配置成converter.setVerifier(new RsaVerifier(publicKey))

@Order(6)//该过滤器优先级需高于WebSecurityConfigurerAdapter才能实现授权码模式
@Configuration
@EnableResourceServer //这个类表明了此应用是OAuth2 的资源服务器,此处主要指定了受资源服务器保护的资源链接
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Bean//需要以公钥作为bean外部接口才能访问受限资源public JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();Resource resource =  new ClassPathResource("public.cert");String publicKey;try {publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));}catch (IOException e) {throw new RuntimeException(e);}//使用converter.setVerifierKey(publicKey);会出现 Cannot convert access token to JSONconverter.setVerifier(new RsaVerifier(publicKey));return converter;}@Beanpublic TokenStore tokenStore() {//配置token模式return new JwtTokenStore(accessTokenConverter());}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.tokenStore(tokenStore());}/** 配置受资源服务器保护的资源链接,仅接受签名校验* */@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable();http.authorizeRequests().antMatchers("/actuator/hystrix.stream").permitAll().anyRequest().authenticated();//校验所有请求}}

提供获取用户信息的接口

@RestController
@RequestMapping("/users")
public class UserController {Logger logger = LoggerFactory.getLogger(UserController.class);//用于获取当前token的用户信息@RequestMapping(value = "/current", method = RequestMethod.GET)public Principal getUser(Principal principal) {logger.info(">>>>>>>>>>>>>>>>>>>>>>>>");logger.info(principal.toString());logger.info("<<<<<<<<<<<<<<<<<<<<<<<<");return principal;}
}

测试:
依次启动sc-eurekaserver、sc-auth-server
访问http://localhost:8761/

配置一个密码登陆模式客户端

注意:
授权码模式已经在代码中实现,需另外配置客户端,该项目未使用该模式这里不细谈。
客户端模式在该项目中无法使用,这是因为我们在CustomTokenEnhancer中自定义令牌信息中使用了用户的相关属性,所以当我们使用客户端模式登陆时由于缺少用户信息授权服务器将会提示空指针异常。

INSERT INTO `oauth_client_details` VALUES ('client', '', '$2a$10$lnIAuLiG3yezER5BfOk//uqPFHLnkwsFV9FxdzoIhATmZEAkK4yXO', 'read,write', 'password,refresh_token', '', 'ROLE_ADMIN,ROLE_USER', '7200', '7200', '{}', '');

等同于如下代码

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(dataSource)//密码登陆模式.withClient("client").secret(new BCryptPasswordEncoder().encode("123456")).authorizedGrantTypes("password", "refresh_token")//允许授权范围.authorities("ROLE_ADMIN","ROLE_USER")//客户端可以使用的权限.scopes( "read", "write").accessTokenValiditySeconds(7200)//token超时时间.refreshTokenValiditySeconds(7200);//token刷新的token超时时间
}

1)访问http://localhost:8078/users/current
资源受保护

2)获取token:http://localhost:8078/oauth/token?username=admin&password=admin&grant_type=password&client_id=client&client_secret=123456

3)使用令牌访问受限资源:

http://localhost:8078/users/current?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJleHAiOjE1NzQwNzQxODMsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImNmNWJjY2JkLWYyMTYtNDQ0MC04NWFlLTdhODgwMDAzZjI5OSIsImNsaWVudF9pZCI6ImNsaWVudCJ9.nz6ygVySdEOpil95H-mbBiZhjQfaz-EKustePdtopzrmuFePHkh8WMNTyeWtfPGAjMIAMYeRDqxvcPg6-nWidhiAXdUogIYMluwi4k-YsFZ8HgzjK7mQNDFaL7kasvRKKcQ0127yQqgG40fmghhInZsj3uGyh4MNMdbIrRBYVgvNsctbrG3StjoHVOAHKTlHxrC9cGQ2D9WV95byarr_UoADFpwad4wux6fnWEOcEgzR2aAmI6YraaZBpPNYrz1LsuHEn2cUJiK-CgbyAt-9PiZzhT-ZIsAK0pCSVi3ALlIepoEMtQSZHlYhT20S_dtK8P3Fp3_-QMecaLfu9Ik2Ow

返回:

{"authorities": [{"authority": "ROLE_ADMIN"}],"details": {"remoteAddress": "0:0:0:0:0:0:0:1","sessionId": null,"tokenValue": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJleHAiOjE1NzQwNzQxODMsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImNmNWJjY2JkLWYyMTYtNDQ0MC04NWFlLTdhODgwMDAzZjI5OSIsImNsaWVudF9pZCI6ImNsaWVudCJ9.nz6ygVySdEOpil95H-mbBiZhjQfaz-EKustePdtopzrmuFePHkh8WMNTyeWtfPGAjMIAMYeRDqxvcPg6-nWidhiAXdUogIYMluwi4k-YsFZ8HgzjK7mQNDFaL7kasvRKKcQ0127yQqgG40fmghhInZsj3uGyh4MNMdbIrRBYVgvNsctbrG3StjoHVOAHKTlHxrC9cGQ2D9WV95byarr_UoADFpwad4wux6fnWEOcEgzR2aAmI6YraaZBpPNYrz1LsuHEn2cUJiK-CgbyAt-9PiZzhT-ZIsAK0pCSVi3ALlIepoEMtQSZHlYhT20S_dtK8P3Fp3_-QMecaLfu9Ik2Ow","tokenType": "Bearer","decodedDetails": null},"authenticated": true,"userAuthentication": {"authorities": [{"authority": "ROLE_ADMIN"}],"details": null,"authenticated": true,"principal": "admin","credentials": "N/A","name": "admin"},"oauth2Request": {"clientId": "client","scope": ["read", "write"],"requestParameters": {"client_id": "client"},"resourceIds": [],"authorities": [],"approved": true,"refresh": false,"redirectUri": null,"responseTypes": [],"extensions": {},"refreshTokenRequest": null,"grantType": null},"credentials": "","clientOnly": false,"principal": "admin","name": "admin"
}

4)刷新令牌:

http://localhost:8078/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJhdGkiOiJjZjViY2NiZC1mMjE2LTQ0NDAtODVhZS03YTg4MDAwM2YyOTkiLCJleHAiOjE1NzQwNzQxODMsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjI1YzU3NWYwLWU1MGEtNDU3MS1hY2ExLWM3ODQwNjc3OWQ2NCIsImNsaWVudF9pZCI6ImNsaWVudCJ9.n_ravNt0iK2JnpoMcS0nf-FS_U1H9lJAYjoR6Mca6r5fGKynoHIxAIqcQsRXw7Qst-w_O6ZCSF_ieAC8MmVxkj6ZKloC7cQglVua0U33PVrheKL6OPcIYa8K5fX_w-VBgXOjoMFN-iRf3aWK19JRvbTXhi9z9_89-u1ciII4lDBoOPVomvKGdXMpXbj_SptAgeh9AYiKBh40vhCBw3g-AntmMZSUfkpkDNiVD2fkyueUyGPCyhMDcHF5xnFuyO6qrBOkwlnrw7kZ96m7346sAb_KQn2u2a1IPU4gysGs7-0zD9rKpAl5d774RWAFGOBXk_CFH2Wol9I6Vmm7R_T5yg`

服务网关sc-zuul-server

pom.xml

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-zuul</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>cn.springcloud.book</groupId><artifactId>cloud1.0-common</artifactId><version>${parent.version}</version></dependency>
</dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><configuration><nonFilteredFileExtensions><nonFilteredFileExtension>cert</nonFilteredFileExtension></nonFilteredFileExtensions></configuration></plugin></plugins>
</build>

application.yml

eureka:client:serviceUrl:defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/instance:prefer-ip-address: true
management:security:enabled: falseendpoints:web:exposure:include: hystrix.stream
feign:hystrix:enabled: true
ribbon:ConnectTimeout: 6000ReadTimeout: 6000MaxAutoRetries: 0 #对第一次请求的服务的重试次数MaxAutoRetriesNextServer: 0 #要重试的下一个服务的最大数量(不包括第一个服务)OkToRetryOnAllOperations: false
zuul:sensitive-headers:ribbonIsolationStrategy: THREADthreadPool:useSeparateThreadPools: truethreadPoolKeyPrefix: zuulgatewaymax:host:max-per-route-connections: 200max-total-connections: 500host:socket-timeout-millis: 5000connect-timeout-millis: 10000
hystrix:threadpool:default:coreSize: 20maximumSize: 50maxQueueSize: -1allowMaximumSizeToDivergeFromCoreSize: truecommand:default:execution:timeout:enabled: falseisolation:thread:interruptOnTimeout: falsetimeoutInMilliseconds: 15000

AuthFilter

这里注意,不能把所有头部信息都在过滤器重新添加一遍,因为zuul本身在转发时会会我们添加头部,重复添加会抛出异常而导致访问失败(post),但是zuul本身会过滤掉一些敏感信息,所以我们需要在配置文件添加:zuul.sensitive-headers=(为空)
题外说一句,要是发现调用错误但是后台没有提示异常记得尝试把熔断注释掉,因为熔断会覆盖异常信息

package cn.springcloud.book.filter;import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;/*** 自定义的鉴权filter*/
public class AuthFilter extends ZuulFilter {private static final Logger logger = LoggerFactory.getLogger(AuthFilter.class);@Overridepublic boolean shouldFilter() {// 判断是否需要进行处理return true;}@Overridepublic Object run() {RequestContext rc = RequestContext.getCurrentContext();authUser(rc);return null;}@Overridepublic String filterType() {return "pre";}@Overridepublic int filterOrder() {return 0;}//将request中Http请求头的所有信息存到一个Map<String, String>中private static Map<String, String> httpRequestToMap(HttpServletRequest request) {Enumeration<String> headerNames = request.getHeaderNames();Map<String, String> headers = new HashMap<>();while (headerNames.hasMoreElements()) {String headerName = headerNames.nextElement();headers.put(headerName, request.getHeader(headerName));}return headers;}//自定义的鉴权处理public static void authUser(RequestContext ctx) {/*      HttpServletRequest request = ctx.getRequest();Map<String, String> header = httpRequestToMap(request);for (Map.Entry<String, String> entry : header.entrySet()) {ctx.addZuulRequestHeader(entry.getKey(), entry.getValue());}*/}}

ZuulFallback

@Component
public class ZuulFallback implements FallbackProvider{@Overridepublic String getRoute() {return "*"; //可以配置指定的路由,值为serviceId,如sc-user-service}@Overridepublic ClientHttpResponse fallbackResponse(String route, Throwable cause) {return new ClientHttpResponse() {@Overridepublic HttpStatus getStatusCode() throws IOException {return HttpStatus.INTERNAL_SERVER_ERROR;}@Overridepublic String getStatusText() throws IOException {return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();}@Overridepublic void close() {}@Overridepublic InputStream getBody() throws IOException {//定义自己的错误信息return new ByteArrayInputStream(("microservice error").getBytes());}@Overridepublic HttpHeaders getHeaders() {HttpHeaders headers = new HttpHeaders();headers.setContentType(MediaType.APPLICATION_JSON);return headers;}@Overridepublic int getRawStatusCode() throws IOException {// TODO Auto-generated method stubreturn HttpStatus.INTERNAL_SERVER_ERROR.value();}};}}

ResourceServerConfiguration

这里注意converter.setVerifier(new RsaVerifier(publicKey))的写法,使用converter.setVerifierKey(publicKey)时,通过令牌访问受限资源会出现 Cannot convert access token to JSON的提示

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {public static final String public_cert = "public.cert";public JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();Resource resource =  new ClassPathResource(public_cert);String publicKey;try {publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));}catch (IOException e) {throw new RuntimeException(e);}//使用converter.setVerifierKey(publicKey);会出现 Cannot convert access token to JSONconverter.setVerifier(new RsaVerifier(publicKey));return converter;}public TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/sc-user-server/**","/sc-auth-server/**","/actuator/hystrix.stream").permitAll().antMatchers("/**").authenticated();}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(tokenStore());}
}

ZuulServerApplication

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
@EnableCircuitBreaker
public class ZuulServerApplication {public static void main(String[] args) {SpringApplication.run(ZuulServerApplication.class, args);}@Beanpublic AuthFilter preRequestFilter() {return new AuthFilter();}
}

用户服务sc-user-server

pom.xml

    <dependencies><dependency><groupId>cn.springcloud.book</groupId><artifactId>cloud1.0-common</artifactId><version>${parent.version}</version></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><configuration><nonFilteredFileExtensions><nonFilteredFileExtension>cert</nonFilteredFileExtension></nonFilteredFileExtensions></configuration></plugin></plugins></build>

application.yml

spring:application:name: sc-user-serverdatasource:driver-class-name: com.mysql.jdbc.Driverurl: jdbc:mysql://localhost:3306/oauth2?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&failOverReadOnly=falseusername: rootpassword: rootjpa:properties:hibernate:dialect: org.hibernate.dialect.MySQL5Dialecthibernate:ddl-auto: updateshow-sql: trueeureka:client:serviceUrl:defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/instance:prefer-ip-address: true
management:security:enabled: falseendpoints:web:exposure:include: hystrix.stream
feign:hystrix:enabled: true
ribbon:ConnectTimeout: 6000ReadTimeout: 6000MaxAutoRetries: 0MaxAutoRetriesNextServer: 0
hystrix:command:default:execution:timeout:isolation:thread:timeoutInMilliseconds: 15000
security:oauth2:resource:jwt:key-uri: http://127.0.0.1:7777/sc-auth-server/oauth/token_key  #如果使用JWT,可以获取公钥用于 token 的验签client:access-token-uri: http://127.0.0.1:7777/sc-auth-server/oauth/token #令牌端点user-authorization-uri: http://127.0.0.1:7777/sc-auth-server/oauth/authorize   #授权端点client-id: client_2client-secret: 123456grant-type: passwordscope: server
server:port: 8087

启动类UserServerApplication

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableCircuitBreaker
public class UserServerApplication {public static void main(String[] args) {SpringApplication.run(UserServerApplication.class, args);}}

基础代码这里不贴了
资源服务器配置ResourceServerConfiguration,开放登陆与注册接口

@EnableGlobalMethodSecurity(prePostEnabled = true)开启方法级别访问权限

@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)//控制方法的访问权限
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {public static final String public_cert = "public.cert";@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();Resource resource =  new ClassPathResource(public_cert);String publicKey;try {publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream()));}catch (IOException e) {throw new RuntimeException(e);}//使用converter.setVerifierKey(publicKey);会出现 Cannot convert access token to JSONconverter.setVerifierKey(publicKey);return converter;}@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}@Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers("/product/**","/registry/**", "/user/login/**","/actuator/hystrix.stream").permitAll().antMatchers("/**").authenticated();//.antMatchers("/**").permitAll();}@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {resources.tokenStore(tokenStore());}
}

接口
(1)注册用户

@RequestMapping(value = "/registry", method = RequestMethod.GET)
public SysUser createUser(@RequestParam("username") String username, @RequestParam("password") String password) {if (StringUtils.isNotEmpty(username) && StringUtils.isNotEmpty(password)) {return userService.create(username, password);}return null;
}

(2)用户登陆获取令牌

@RequestMapping("/user")
@RestController
public class UserController {@Autowiredprivate SysUserRepository sysUserRepository;@Autowiredprivate OAuth2ClientProperties oAuth2ClientProperties;@Autowiredprivate OAuth2ProtectedResourceDetails oAuth2ProtectedResourceDetails;@Autowiredprivate RestTemplate restTemplate;@RequestMapping("/login")public ResponseEntity<OAuth2AccessToken> login(@Valid UserLoginParamDto loginDto, BindingResult bindingResult) throws Exception {if (bindingResult.hasErrors())throw new Exception("登录信息错误,请确认后再试");SysUser user = sysUserRepository.getUserByName(loginDto.getUsername());if (null == user)throw new Exception("用户为空,出错了");if (!BPwdEncoderUtil.matches(loginDto.getPassword(), user.getPassword()))throw new Exception("密码不正确");String client_secret = oAuth2ClientProperties.getClientId()+":"+oAuth2ClientProperties.getClientSecret();client_secret = "Basic "+Base64.getEncoder().encodeToString(client_secret.getBytes());HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set("Authorization",client_secret);//授权请求信息MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.put("username", Collections.singletonList(loginDto.getUsername()));map.put("password", Collections.singletonList(loginDto.getPassword()));map.put("grant_type", Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));map.put("scope", oAuth2ProtectedResourceDetails.getScope());//HttpEntityHttpEntity httpEntity = new HttpEntity(map,httpHeaders);//获取 Token(注意先配置security)return restTemplate.exchange("http://sc-auth-server/oauth/token", HttpMethod.POST,httpEntity,OAuth2AccessToken.class);//访问/oauth/token获取令牌}
}

(3)通过令牌获取用户信息

@GetMapping("/getPrinciple")
public OAuth2Authentication getPrinciple(OAuth2Authentication oAuth2Authentication, Principal principal, Authentication authentication) {logger.info(oAuth2Authentication.getUserAuthentication().getAuthorities().toString());logger.info(oAuth2Authentication.toString());logger.info("principal.toString() " + principal.toString());logger.info("principal.getName() " + principal.getName());logger.info("authentication: " + authentication.getAuthorities().toString());return oAuth2Authentication;
}

(4)测试权限控制接口

    @PreAuthorize("hasAnyRole('ADMIN')")@RequestMapping("/helloRole")public String helloRole() {return "hello you.by ADMIN";}

(5)开放测试接口

@RequestMapping("/product/{id}")
public String getProduct(@PathVariable String id) {String dbpasswor = "$2a$10$HBX6q6TndkgMxhSEdoFqWOUtctaJEMoXe49NWh8Owc.4MTunv.wXa";logger.info("判断两个密码是否相等 " + (BPwdEncoderUtil.matches("123456", dbpasswor)));return "product id : " + id;
}

测试
依次启动sc-eurekaserver、sc-auth-server、sc-zuul-server、sc-user-server
访问http://localhost:8761/

(1)测试通过网关访问授权服务器
获取令牌

http://localhost:7777/sc-auth-server/oauth/token?username=admin&password=admin&grant_type=password&client_id=client&client_secret=123456

通过令牌访问受限接口

http://localhost:7777/sc-auth-server/users/current?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJleHAiOjE1NzUzNzI4NTcsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6IjhiZmJmMTI2LTkxZmItNGQxNy05MDk0LTM5NWQ4MjZmYWJjMSIsImNsaWVudF9pZCI6ImNsaWVudCJ9.SGkzpqjMqftcsWdZshxGcA7VwbbGX3zE-fHBAvLF1bCtMctW3Fsfjfh_HGiU_gU1RyV7eiETFCaQRPl6zdO4AnBoXyav20DdBOq7D_cjgqp-aPtGu7swZFiRRg1AKbKyYmRRHTOeYEp2XF7uScD1RacqeECVt6Pj-Q9B7YNDlY_YiY94n1zCJLrhnscu1K2L-rvGx2G2AMZrVbKtZD1K1VTGNZXPz99Us9Dnow8-Cu5-w0U8x4dvQl1dlPgG7NL0dqX4_mNWUPcX4bMFqxsFY7-8cLQjT2LtzgIhJid9zY4RmqpXfujbOS_EP_o-KCdmA64aEzCkcNQgCUweVZKRlA

刷新令牌

http://localhost:7777/sc-auth-server/oauth/token?grant_type=refresh_token&client_id=client&client_secret=123456&refresh_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJhdGkiOiJiMTY0MWM1Mi1iNjdlLTQ5ZmYtOWY4OS1lNmI3YWUzYjQyMTUiLCJleHAiOjE1NzUwMjYyNTgsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6ImMzY2IxYTNkLTJmY2EtNDk1MS1iZDkxLWY3NzcxNjlkMTE5MSIsImNsaWVudF9pZCI6ImNsaWVudCJ9.GhEHazkUZ8-KLpSBVugFvHoCYdUs-l8OSyVIEYBSXZPLY1ku6j4Fu8laRJy3JnovmmL72erPwdPXIcshcqoflZmE0FJn29ibIxyNvNqd5BbJroidwQJNQ_0u0RaMw0r1DQDtKMDBQv2Ajc6rgAcqR874duMb5Ehx9WUlmw5zEmF7SEKSP1TT_4MnVfwDumQHeBafrnVS7nqddgEckvMaTXbuhyY-0cr8C_56W1bE6tW_sbTF9HMly3nH65jlWiHRR43iAyGf2juEmFoKg2VwaKDTwBheiLwX2RTMZ5hhMhP_XkT2Ea6-2sjdVegKeNLcl7AYS47yq6PyKf4FCTSnyw

如果代码正确返回结果应和授权服务器一章的测试结果一致
(2)访问sc-user-server的开放接口

http://localhost:7777/sc-user-server/product/Hello

返回:

(3)注册用户

http://localhost:7777/sc-user-server/registry?username=OneTest&password=OneTest

返回:

(4)通过该用户获取令牌

http://localhost:7777/sc-user-server/user/login?username=OneTest&password=OneTest

返回:

{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJPbmVUZXN0Iiwic2NvcGUiOlsic2VydmVyIl0sInJvbGVzIjpbXSwib3JnYW5pemF0aW9uIjoiT25lVGVzdCIsImV4cCI6MTU3NTA2MzUzMCwidXNlck5hbWUiOiJPbmVUZXN0IiwianRpIjoiOGVhM2ZkMjYtNTI0NS00MDZhLWI3YjctOWUwMDU5MTg4NjcwIiwiY2xpZW50X2lkIjoiY2xpZW50XzIifQ.esPN-hxIMYCyNFROeQvfDv1nwwxasiYilT51XY3VlalXJdyr4soKFw1Ivy-VV1WQUgcbl1aUD0oejHrd89XobHEym8vF2EY04DwDgiLpl7-8ORxqxMnlqeqoT95jURaMTRhHRyls6e6mv0JOecUE0LU_kOlqQjw9z7XiQTzVjdAku0zMstQjyV078vneoiYE-9xEyHG-_-eVLfXvYztVx86-EJbCPK4jEJndU6dCL0VKa_tUF6q_gMWzAzNYktWuBnjL_nQkwiFQ1Vq4b8l5S--3ZL60Ql-Mm_tuAMIdxH5H7TXdDPv4ewVxHDbUOO-3yfBWknjNzhqY5syHlQ_uWA","token_type":"bearer","refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJPbmVUZXN0Iiwic2NvcGUiOlsic2VydmVyIl0sInJvbGVzIjpbXSwib3JnYW5pemF0aW9uIjoiT25lVGVzdCIsImF0aSI6IjhlYTNmZDI2LTUyNDUtNDA2YS1iN2I3LTllMDA1OTE4ODY3MCIsImV4cCI6MTU3NzYxMjMzMCwidXNlck5hbWUiOiJPbmVUZXN0IiwianRpIjoiYjA4YzU5MGMtMGI5NC00MDY5LTg3ZWUtYjVhNjc4ODE4ZWJiIiwiY2xpZW50X2lkIjoiY2xpZW50XzIifQ.bb9tpoMVQfQj6DKg9MgKBm6fbcI_iIzbhI0vHLb-5RVwYuIFTLC5m7wjMaPs_MZHpQ6YI9by6CR5V68zw1P-sDJmS8n1JBnnhAh4RtzTbqJln7DB4k3ZunoubKERXZCnlQgF_x1YfqyZj8Fly-XvrrUevIyY68vVZJCVXWf2-yojX5Y-WowA_Sj2bnquBFDNuhisc-Lz4eR9mNLuQptu33ewRUTUf4NfwOCQ9wtZbvqknavy0jjZATXWGv2aUOJeYwy3RMfBkYjPSdvQbyLUXtCwc4wJ2aglfu8j8qmMU8gHnAkVHQEBIkOgUD18K7d3By-rZsEt_2T9TKnsi6GnUQ","expires_in":43198,"scope":"server","roles":[],"organization":"OneTest","userName":"OneTest","jti":"8ea3fd26-5245-406a-b7b7-9e0059188670"}

(5)通过令牌访问受限资源

http://localhost:7777/sc-user-server/getPrinciple?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJPbmVUZXN0Iiwic2NvcGUiOlsic2VydmVyIl0sInJvbGVzIjpbXSwib3JnYW5pemF0aW9uIjoiT25lVGVzdCIsImV4cCI6MTU3NTQ2OTgyMiwidXNlck5hbWUiOiJPbmVUZXN0IiwianRpIjoiMmU4YWI5YTgtYzZjYi00MmY5LWE4OTktYjZjM2M0MmVmZDQwIiwiY2xpZW50X2lkIjoiY2xpZW50XzIifQ.Jb9EQKtWeCE_50N2hQrGlJnY2FBQAjhOBLEhXOpVWekvGGpTUZ7YiapA8M9mqm49Nwg9cFYYSP_sUcm-BGM5sPZTJ5HkclVBd7NHJPpRyALB6rOIr0jR9i9b8AgwV0F-hPsh3cwaf2dZR3aARmLwM-zMZFnsD7-J0fbgCLIdQTcWb8XfRqULukYRp_D8ql4HV5IYg7zmQsNmgYghvo3Nf50PKr7q1ivCxoFGjOXyrm0uoKZZ5DZB3aXcQHDR_Zg8AWsJfctQ-YGexxY6jRPUKuOSI0_LPNSZ8VnfYH8DKfV0BZnnnH40A7OdN3GaXg-1i1bkgv-6yF4l5yvbVKknww

返回:

{"authorities":[],"details":{"remoteAddress":"192.168.90.111","sessionId":null,"tokenValue":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJPbmVUZXN0Iiwic2NvcGUiOlsic2VydmVyIl0sInJvbGVzIjpbXSwib3JnYW5pemF0aW9uIjoiT25lVGVzdCIsImV4cCI6MTU3NTQ2OTgyMiwidXNlck5hbWUiOiJPbmVUZXN0IiwianRpIjoiMmU4YWI5YTgtYzZjYi00MmY5LWE4OTktYjZjM2M0MmVmZDQwIiwiY2xpZW50X2lkIjoiY2xpZW50XzIifQ.Jb9EQKtWeCE_50N2hQrGlJnY2FBQAjhOBLEhXOpVWekvGGpTUZ7YiapA8M9mqm49Nwg9cFYYSP_sUcm-BGM5sPZTJ5HkclVBd7NHJPpRyALB6rOIr0jR9i9b8AgwV0F-hPsh3cwaf2dZR3aARmLwM-zMZFnsD7-J0fbgCLIdQTcWb8XfRqULukYRp_D8ql4HV5IYg7zmQsNmgYghvo3Nf50PKr7q1ivCxoFGjOXyrm0uoKZZ5DZB3aXcQHDR_Zg8AWsJfctQ-YGexxY6jRPUKuOSI0_LPNSZ8VnfYH8DKfV0BZnnnH40A7OdN3GaXg-1i1bkgv-6yF4l5yvbVKknww","tokenType":"Bearer","decodedDetails":null},"authenticated":true,"userAuthentication":{"authorities":[],"details":null,"authenticated":true,"principal":"OneTest","credentials":"N/A","name":"OneTest"},"credentials":"","oauth2Request":{"clientId":"client_2","scope":["server"],"requestParameters":{"client_id":"client_2"},"resourceIds":[],"authorities":[],"approved":true,"refresh":false,"redirectUri":null,"responseTypes":[],"extensions":{},"refreshTokenRequest":null,"grantType":null},"clientOnly":false,"principal":"OneTest","name":"OneTest"}

(6)测试权限控制接口
访问该接口需要ADMIN角色(目前我们注册的用户没有任何角色和权限)

http://localhost:7777/sc-user-server/helloRole?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJPbmVUZXN0Iiwic2NvcGUiOlsic2VydmVyIl0sInJvbGVzIjpbXSwib3JnYW5pemF0aW9uIjoiT25lVGVzdCIsImV4cCI6MTU3NTQ2OTgyMiwidXNlck5hbWUiOiJPbmVUZXN0IiwianRpIjoiMmU4YWI5YTgtYzZjYi00MmY5LWE4OTktYjZjM2M0MmVmZDQwIiwiY2xpZW50X2lkIjoiY2xpZW50XzIifQ.Jb9EQKtWeCE_50N2hQrGlJnY2FBQAjhOBLEhXOpVWekvGGpTUZ7YiapA8M9mqm49Nwg9cFYYSP_sUcm-BGM5sPZTJ5HkclVBd7NHJPpRyALB6rOIr0jR9i9b8AgwV0F-hPsh3cwaf2dZR3aARmLwM-zMZFnsD7-J0fbgCLIdQTcWb8XfRqULukYRp_D8ql4HV5IYg7zmQsNmgYghvo3Nf50PKr7q1ivCxoFGjOXyrm0uoKZZ5DZB3aXcQHDR_Zg8AWsJfctQ-YGexxY6jRPUKuOSI0_LPNSZ8VnfYH8DKfV0BZnnnH40A7OdN3GaXg-1i1bkgv-6yF4l5yvbVKknww

返回:

使用admin用户登陆拿到令牌,再访问该接口

http://localhost:7777/sc-user-server/user/login?username=admin&password=admin
http://localhost:7777/sc-user-server/helloRole?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbInNlcnZlciJdLCJyb2xlcyI6W3siYXV0aG9yaXR5IjoiUk9MRV9BRE1JTiJ9XSwib3JnYW5pemF0aW9uIjoiYWRtaW4iLCJleHAiOjE1NzU0NzAzNzQsInVzZXJOYW1lIjoiYWRtaW4iLCJhdXRob3JpdGllcyI6WyJST0xFX0FETUlOIl0sImp0aSI6Ijc5OTc1NGM5LWU5Y2UtNDc5Mi05YjVlLWUzODYwNGQ2ODA4MyIsImNsaWVudF9pZCI6ImNsaWVudF8yIn0.d_MmIr05z_DsEhW9eb1LHLr90E4EWw3lnJzIo2iI9ZAfAB80HkkwvKMT4e2_oVVTeB5zvQtdJdjHf2OgaXm8oTc0ktzdpfi83jwVLBhH19Obpi1LEtgupI6dYh7KkSzpBhCwn_ObFKBDz2H_xJE58c5_dwA_Id1jpqvS7vjK0egoZeyPShIp-dq0GHwzUMntfl28XN7qLi9q5NBPvFl7bgKqmiycnaNBBRfQBpOntZWiO_BRZD8aEvc8kNwO2j5ywzA3mfFaXUW26jKEb4hPfj5_ibeN-S5YInIoe33wTqEdeZysq_qJN2EkAFzf0D_-QuLT9w8AH_PG05MteBfx-g

返回:

至此Zuul整合SpringSecurityOAuth2(JWT)完成

熔断监控sc-hystrix-dashboard

pom.xml

<dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId></dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-turbine</artifactId></dependency><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>
</dependencies>

application.yml

server:port: 9099
spring:cloud:config:label: masteruri: http://localhost:9090name: config-infoprofile: devapplication:name: sc-hystrix-dashboard
eureka:client:serviceUrl:defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8761}/eureka/instance:prefer-ip-address: true
management:security:enabled: falseendpoints:web:exposure:include: hystrix.stream
turbine:appConfig: sc-auth-server,sc-user-server,sc-zuul-service    #指定聚合哪些集群,多个使用”,”分割,默认为default。可使用http://.../turbine.stream?cluster={clusterConfig之一}访问clusterNameExpression: "'default'"

HystrixDashboardTurbineApplication.java

@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrixDashboard
@EnableTurbine
public class HystrixDashboardTurbineApplication {public static void main(String[] args) {SpringApplication.run(HystrixDashboardTurbineApplication.class, args);}}

由于HystrixDashboard是对于熔断进行监控,为了方便我们进行测试,所以我们对sc-user-server的登陆接口添加熔断器(application.yml中我们已经添加过熔断器的配置了)

@RequestMapping("/login")
@HystrixCommand(fallbackMethod = "loginError")
public ResponseEntity<OAuth2AccessToken> login(@Valid UserLoginParamDto loginDto, BindingResult bindingResult) throws Exception {if (bindingResult.hasErrors())throw new Exception("登录信息错误,请确认后再试");SysUser user = sysUserRepository.getUserByName(loginDto.getUsername());if (null == user)throw new Exception("用户为空,出错了");if (!BPwdEncoderUtil.matches(loginDto.getPassword(), user.getPassword()))throw new Exception("密码不正确");String client_secret = oAuth2ClientProperties.getClientId()+":"+oAuth2ClientProperties.getClientSecret();client_secret = "Basic "+Base64.getEncoder().encodeToString(client_secret.getBytes());HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set("Authorization",client_secret);//授权请求信息MultiValueMap<String, String> map = new LinkedMultiValueMap<>();map.put("username", Collections.singletonList(loginDto.getUsername()));map.put("password", Collections.singletonList(loginDto.getPassword()));map.put("grant_type", Collections.singletonList(oAuth2ProtectedResourceDetails.getGrantType()));map.put("scope", oAuth2ProtectedResourceDetails.getScope());//HttpEntityHttpEntity httpEntity = new HttpEntity(map,httpHeaders);//获取 Token(注意先配置security)return restTemplate.exchange("http://sc-auth-server/oauth/token", HttpMethod.POST,httpEntity,OAuth2AccessToken.class);//访问/oauth/token获取令牌}//登陆熔断
public ResponseEntity<OAuth2AccessToken> loginError(@Valid UserLoginParamDto loginDto, BindingResult bindingResult) throws Exception {throw new Exception("登陆服务访问失败");
}

测试
http://localhost:9099/hystrix

在第一个输入框输入http://localhost:9099/turbine.stream,点击MonitorStream获取集群监控列表
输入http://localhost:port/actuator/hystrix.stream,点击MonitorStream获取获取单个熔断监控
访问http://localhost:7777/sc-user-server/user/login?username=OneTest&password=OneTest
查看集群监控列表

补充:使用Feign/RestTemplate调用服务时的权鉴处理与服务调用案例

权鉴处理

由于项目整合了CloudOAuth2,所以当我们使用Feign/RestTemplate进行服务间的相互调用时我们需要考虑到令牌传递的问题
SpringSecurityOAuth2通过token访问受限资源一般有两种方式,一种是通过头部添加Authorization凭证,一种是直接添加access_token作为参数,但是在SpringSecurityOAuth2框架中后者最终也是转换成Authorization凭证用于权鉴,所以我们只要在Feign/RestTemplate的过滤器中传递头部信息就能解决服务内部资源获取的权鉴问题
FeignUserContextInterceptor

public class FeignUserContextInterceptor implements RequestInterceptor {@Overridepublic void apply(RequestTemplate template) {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();//Feign传递头部信息Enumeration<String> headerNames = request.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String name = headerNames.nextElement();String values = request.getHeader(name);template.header(name, values);}}}}

RestTemplateUserContextInterceptor

public class RestTemplateUserContextInterceptor implements ClientHttpRequestInterceptor {@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)throws IOException {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest req = attributes.getRequest();//RestTemplate传递头部信息HttpHeaders headers = request.getHeaders();Enumeration<String> headerNames = req.getHeaderNames();if (headerNames != null) {while (headerNames.hasMoreElements()) {String name = headerNames.nextElement();String values = req.getHeader(name);headers.add(name, values);}}//RestTemplate实现session共享String sessionId = RequestContextHolder.currentRequestAttributes().getSessionId();if (null != sessionId) {request.getHeaders().add("Cookie", "SESSION=" + sessionId);}return execution.execute(request, body);}
}

直接修改完发现一个问题,RequestContextHolder.getRequestAttributes()返回为null,查看源码后发现RequestContextHolder.getRequestAttributes()方法是从ThreadLocal变量里面取得相应信息的


ThreadLocal是当前线程中的局部变量,当hystrix断路器的隔离策略为THREAD时服务于服务间的线程是不同的,因此我们在Feign/RestTemplate过滤器中无法取得ThreadLocal中的值。一种解决方案是将hystrix隔离策略换为SEMAPHORE(不推荐),一种是自定义隔离策略,这里我们采用第二种方案
在cloud1.0-common工程中添加自定义隔离策略
SpringCloudHystrixConcurrencyStrategy

public class SpringCloudHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {private HystrixConcurrencyStrategy delegateHystrixConcurrencyStrategy;//核心代码,自定义处理@Overridepublic <T> Callable<T> wrapCallable(Callable<T> callable) {return new HystrixThreadCallable<>(callable, RequestContextHolder.getRequestAttributes(),SwapContextHolder.context.get());}public SpringCloudHystrixConcurrencyStrategy() {init();}private void init() {try {this.delegateHystrixConcurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy();if (this.delegateHystrixConcurrencyStrategy instanceof SpringCloudHystrixConcurrencyStrategy) {return;}HystrixCommandExecutionHook commandExecutionHook = HystrixPlugins.getInstance().getCommandExecutionHook();HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy();HystrixPlugins.reset();HystrixPlugins.getInstance().registerConcurrencyStrategy(this);HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);HystrixPlugins.getInstance().registerPropertiesStrategy(propertiesStrategy);}catch (Exception e) {throw e;}}@Overridepublic ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,HystrixProperty<Integer> corePoolSize,HystrixProperty<Integer> maximumPoolSize,HystrixProperty<Integer> keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue) {return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize,keepAliveTime, unit, workQueue);}@Overridepublic ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,HystrixThreadPoolProperties threadPoolProperties) {return this.delegateHystrixConcurrencyStrategy.getThreadPool(threadPoolKey, threadPoolProperties);}@Overridepublic BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {return this.delegateHystrixConcurrencyStrategy.getBlockingQueue(maxQueueSize);}@Overridepublic <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {return this.delegateHystrixConcurrencyStrategy.getRequestVariable(rv);}}

HystrixThreadCallable

public class HystrixThreadCallable<S> implements Callable<S>{private final RequestAttributes requestAttributes;  private final Callable<S> delegate;private Map<String, Object> params;public HystrixThreadCallable(Callable<S> callable, RequestAttributes requestAttributes,Map<String, Object> params) {  this.delegate = callable; this.requestAttributes = requestAttributes;  this.params = params;}@Override  public S call() throws Exception {try {RequestContextHolder.setRequestAttributes(requestAttributes);SwapContextHolder.context.set(params);//System.out.println(">>>>>>>>>>>>>>>>>>."+params+".<<<<<<<<<<<<<<<<<<<<");return delegate.call();  } finally {RequestContextHolder.resetRequestAttributes();SwapContextHolder.context.remove();}  }  }

在HystrixThreadCallable中我们除了将RequestAttributes进行线程间的传递我们还传入了SwapContextHolder内的资源,该对象主要是为了方便后续我们在服务于服务间传递变量而做的一个扩展
SwapContextHolder

public class SwapContextHolder {public static ThreadLocal<Map<String, Object>> context = new ThreadLocal<Map<String, Object>>();public static Map<String, Object> currentSwap() {return context.get();}public static void set(Map<String, Object> swap) {context.set(swap);}public static void shutdown() {context.remove();}}

这里注意一点,我们原先在zuul上添加了zuul.sensitive-headers.ribbonIsolationStrategy=THREAD的配置,需要把这里注释掉,不然自定义策略在zuul网关将无法生效

feign

cloud1.0-user-server工程

DataService.java

@FeignClient(name = "sc-data-service", fallback=UserClientFallback.class)
public interface DataService {@RequestMapping("/getService")public String getService(@RequestParam(value = "request")String  request);//返回配置文件内配置的默认用户@GetMapping("/getDefaultUser")public String getDefaultUser();@RequestMapping(value = "/getProviderData")public List<String> getProviderData();@RequestMapping(value = "/getUser")public String getUser();
}

熔断UserClientFallback.java

@Component
public class UserClientFallback implements DataService{@Overridepublic String getService(@RequestParam(value = "request")String  request) {return "hello," +request+", this messge send failed ";}@Overridepublic String getDefaultUser() {return new String("get getDefaultUser failed");}@Overridepublic List<String> getProviderData() {List<String> list = new ArrayList<>();list.add("get getProviderData failed");return list;}@Overridepublic String getUser() {return new String("get getUser failed");}}

UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate DataService dataService;......@Overridepublic String getService(String request) {return dataService.getService(request);}@Overridepublic String getDefaultUser() {return dataService.getDefaultUser();}@Overridepublic String getUser() {return dataService.getUser();}
}

RestTemplate

cloud1.0-user-server工程
UserServiceImpl.java

@Service
public class UserServiceImpl implements UserService {@Autowiredprivate RestTemplate restTemplate;......@Override@HystrixCommand(fallbackMethod = "getProviderDataError")public List<String> getProviderData() {List<String> result = restTemplate.getForObject("http://sc-data-service/getProviderData", List.class);return result;}public List<String> getProviderDataError() {List<String> list = new ArrayList<String>();list.add("getFail");return list;}@Override@HystrixCommand(fallbackMethod = "getProviderDataByPostError")public List<String> getProviderDataByPost() {MultiValueMap<String, Object> param = new LinkedMultiValueMap<String, Object>();param.add("serviceName", "serviceName");param.add("serviceValue", "serviceValue");
//      HttpHeaders headers = new HttpHeaders();
//      headers.add("Content-Type", "application/x-www-form-urlencoded");
//      HttpEntity<MultiValueMap<String, Object>> formEntity = new HttpEntity<MultiValueMap<String, Object>>(param, headers);//含有请求头//List<String> result = restTemplate.postForObject("http://sc-data-service/getProviderDataByPost", formEntity, List.class);//不含请求头List<String> result = restTemplate.postForObject("http://sc-data-service/getProviderDataByPost", param, List.class);return result;}public List<String> getProviderDataByPostError() {List<String> list = new ArrayList<String>();list.add("getFail");return list;}@Override@HystrixCommand(fallbackMethod = "getUserByRestTemplateError")public String getUserByRestTemplate() {return restTemplate.postForObject("http://sc-data-service/getUser", null, String.class);}public String getUserByRestTemplateError() {return new String("getUserByRestTemplateError");}
}

至此一个微服务的基本架构整合完成

SpringCloud基础权限框架搭建(1)-Zuul整合SpringSecurityOAuth2(JWT)+Turbine-附源码相关推荐

  1. java计算机毕业设计ssm基于SSM框架的旅游订票系统s0s38(附源码、数据库)

    java计算机毕业设计ssm基于SSM框架的旅游订票系统s0s38(附源码.数据库) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + Mysql + HBuilderX(Webstor ...

  2. 后台基础权限框架搭建实现[木字楠博客]

    文章目录 1.项目整合SpringSecurity 1.1.引入SpringSecurity依赖 1.2.启动测试 1.3.自定义实体类继承UserDetails 1.4.自定义配制文件 1.5.重写 ...

  3. springcloud 图片和数据一起提交_SpringCloud网上商城系统(附源码及教程)

    简介 设计精良的网上商城系统,包括前端.后端.数据库.负载均衡.数据库缓存等,使用SpringCloud框架,基于Java开发.该项目可部署到服务器上,不断完善中! 预览 ​ 功能说明 用户微服务 用 ...

  4. 使用Xamarin开发手机聊天程序 -- 基础篇(大量图文讲解 step by step,附源码下载)

    如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,Xamarin已经被微 ...

  5. 手把手搭建Java个人博客管理系统【附源码】(毕设)

    文末源码和视频教学 一.项目简介 ​ [个人博客管理系统],基于JavaWeb实现的个人博客管理系统 二.技术实现 IOC容器:spring boot2 Web框架:SpringMVC ORM框架:M ...

  6. Android开发之6.0动态权限工具类(高德地图官方扣出来的)附源码

    感觉这个权限申请类写的比较好. 使用方法:activity直接继承就可以了. 看下mainactivity.java package com.qfy.permissiondemo;import and ...

  7. 【RuoYi框架】RuoYi框架学习超简单案例 - 新闻管理系统(附源码)

    文章目录 前言 一.需求 二.数据库设计 1.新闻菜单表(树表) 1.新闻内容表 三.运行RuoYi项目 1.创建数据库,运行sql文件 2.启动运行RuoYi项目 四.生成代码 1.添加字典(菜单显 ...

  8. cyq.data mysql_CYQ.Data4.5.5下载-CYQ.Data数据框架整套下载4.5.5 免费版【附源码】-东坡下载...

    CYQ.Data是路过秋天分享的一款支持超多数据库的数据框架类库,它界面简洁,操作简单,功能强大易用,支持多语言,多数据库,RSS,AOP等功能,小编这附上CYQ.Data4.5.5整套下载. CYQ ...

  9. 基于Python的Flask框架开发的在线电影网站系统(附源码)

    来源丨网络 今天给大家分享的是基于Python的Flask框架开发的在线电影网站系统. 项目介绍 项目介绍:网站前端采用HTML5编写,使用Bootstrap前端开发框架,后端使用Python3语言编 ...

  10. Springboot+mysql+基于VUE框架的商城综合项目设计与实现 毕业设计-附源码111612

    基于VUE框架的商城综合项目设计与实现 摘 要 随着科学技术的飞速发展,社会的方方面面.各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,商城综合项目当然也不能排除在外.商城综合项目 ...

最新文章

  1. mysql.err日志分析_Mysql日志解析
  2. Python字符串方法:字符串拼接、截取、转数字
  3. Docker 和 Kubernetes 从听过到略懂:给程序员的旋风教程
  4. Sealed,new,virtual,abstract与override的区别
  5. 微信小程序实践_1前言
  6. 中国增材制造市场投资前景与发展投资战略调研报告2022-2028年版
  7. java 二叉树的高度_Java实现二叉树的建立、计算高度与递归输出操作示例
  8. 从没想过会有一个这样的机会|大疆招聘
  9. 信息学奥赛一本通 1188:菲波那契数列(2) | OpenJudge NOI 2.3 1760:菲波那契数列(2)
  10. 如何查看电脑显卡配置_无需软件!直接查看电脑配置方法
  11. 早晚我要把它们都干掉!!!!!!!
  12. avalon2学习教程14动画使用
  13. 华南理工大学控制工程考研经验分享
  14. 全面系统学习机房精密空调设计、选型、安装、维保
  15. Pano2VR热点热区热点替换
  16. IBM Websphere CEI Configuration
  17. 时间序列分析中的增长率——同比与环比
  18. malloc(): smallbin double linked list corrupted:
  19. (译)在cocos2d里面如何使用物理引擎box2d:弹球
  20. 粉末成型工艺(粉末冶金粉末注射成型)

热门文章

  1. 内核获取网络设备的网桥接口
  2. FAT32/exFAT/NTFS,三种U盘格式的区别
  3. U盘文件系统格式快速转换
  4. win10任务栏无反应解决办法
  5. 微信公众号H5网页调用微信支付
  6. 2017年下半年综合素质作文
  7. Au:持续性噪音降噪方法
  8. 【DeepLearning】深入理解dropout正则化
  9. PPT设置自动保存时间 mac_如何让你的PPT变得高大上?
  10. 2019ug最新版本是多少_UGNX将在2019年隆重发布最新版本,也是最后一个版本,让你我都想不到的是..........