Spring Cloud 基础(Greenwich版)

Spring Cloud 基础(Greenwich版)SpringCloud 基础 Greenwich 版 基础环境 parent demopom xml parent groupId org springframew boot groupId artifactId spring boot starter parent artifactId version 2 1 6 RELEASE version parent

Spring Cloud 基础(Greenwich版)

基础环境

parent-demo

pom.xml

<parent> <groupId>org.springframework.boot 
     groupId> <artifactId>spring-boot-starter-parent 
      artifactId> <version>2.1.6.RELEASE 
       version>  
        parent> <groupId>com.demo 
         groupId> <artifactId>parent-demo 
          artifactId> <version>1.0-SNAPSHOT 
           version> <dependencies> <dependency> <groupId>org.springframework.boot 
            groupId> <artifactId>spring-boot-starter 
             artifactId>  
              dependency> <dependency> <groupId>org.springframework.boot 
               groupId> <artifactId>spring-boot-starter-web 
                artifactId>  
                 dependency> <dependency> <groupId>org.projectlombok 
                  groupId> <artifactId>lombok 
                   artifactId>  
                    dependency>  
                     dependencies> 

user-demo-01

pom.xml

<parent> <groupId>com.demo 
     groupId> <artifactId>parent-demo 
      artifactId> <version>1.0-SNAPSHOT 
       version>  
        parent> <artifactId>user-demo-01 
         artifactId> 

application.yml

spring: application: name: user-demo datasource: url: jdbc:mysql:///bank?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC username: root password:  server: port: 8091 

controller

@RestController @RequestMapping("/user") public class UserController { 
    @Autowired private UserDao userDao; @RequestMapping("/{id}") public User findUser(@PathVariable Integer id) { 
    return userDao.findById(id).orElse(null); } } 

gift-demo

pom.xml

<parent> <groupId>com.demo 
     groupId> <artifactId>parent-demo 
      artifactId> <version>1.0-SNAPSHOT 
       version>  
        parent> <artifactId>gift-demo 
         artifactId> 

application.yml

spring: application: name: gift-demo server: port: 8090 

gift-demo 利用 RestTemplate 远程调用 user-demo-01 的接口

@Bean public RestTemplate restTemplate() { 
    return new RestTemplate(); } 
@RestController @RequestMapping("/gift") public class GiftController { 
    @Autowired private RestTemplate restTemplate; @RequestMapping("/{id}") public User findUser(@PathVariable Integer id) { 
    String url = "http://127.0.0.1:8091/user/{id}"; URI uri = UriComponentsBuilder.fromHttpUrl(url).build(id); return restTemplate.getForObject(uri, User.class); } } 

Eureka 服务注册中心

parent-demo 的 pom.xml 加入

<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-dependencies 
      artifactId> <version>Greenwich.RELEASE 
       version> <type>pom 
        type> <scope>import 
         scope>  
          dependency>  
           dependencies>  
            dependencyManagement> 

Eureka Server 服务端

pom.xml

<dependency> <groupId>org.glassfish.jaxb 
     groupId> <artifactId>jaxb-runtime 
      artifactId>  
       dependency> 

Eureka服务器所依赖的JAXB模块已在JDK 11中删除。如果要在运行Eureka服务器时使用JDK 11,则必须在POM或Gradle文件中包括这些依赖项。

<dependencies> <dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-netflix-eureka-server 
      artifactId>  
       dependency>  
        dependencies> 
Eureka Server 高可用

本地搭建的时候修改 windows 的 hosts 文件 C:\Windows\System32\drivers\etc

application.yml

server: port: 8701 spring: application: name: eureka-server eureka: instance: hostname: eureka-server-01 client: service-url: defaultZone: http://eureka-server-02:8702/eureka/ ============================================================================================================ server: port: 8702 spring: application: name: eureka-server eureka: instance: hostname: eureka-server-02 client: service-url: defaultZone: http://eureka-server-01:8701/eureka/ 
@SpringBootApplication @EnableEurekaServer public class EurekaServer01Application { 
    public static void main(String[] args) { 
    SpringApplication.run(EurekaServer01Application.class, args); } } ============================================================================================================ @SpringBootApplication @EnableEurekaServer public class EurekaServer02Application { 
    public static void main(String[] args) { 
    SpringApplication.run(EurekaServer02Application.class, args); } } 

Eureka Client 客户端

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-netflix-eureka-client 
      artifactId>  
       dependency> 

application.yml

eureka: client: service-url: defaultZone: http://eureka-server-01:8701/eureka,http://eureka-server-02:8702/eureka 

Ribbon 负载均衡

复制一份 user-demo-01

RestTemplate 上添加注解 @LoadBalanced

@Bean @LoadBalanced public RestTemplate restTemplate() { 
    return new RestTemplate(); } 

设置 user-demo 微服务的请求方式为随机

user-demo: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule 

Hystrix 熔断器

操作 gift-demo 微服务(服务的消费方)

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-netflix-hystrix 
      artifactId>  
       dependency> 

启动类要添加 @EnableCircuitBreaker 注解

@RequestMapping("/portTimeout") // 设置该方法的请求超时时间为 2000ms;默认 1000ms @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") // Hystrix 的仓壁模式:  // 设置 threadPoolKey 以便每个方法都会有自己的线程池;否则所有标注了 @HystrixCommand 的方法公用一个线程池 @HystrixCommand( threadPoolKey = "portTimeout", threadPoolProperties = { 
    @HystrixProperty(name = "coreSize",value = "1"), @HystrixProperty(name = "maxQueueSize",value = "20") }, // 兜底方法 fallbackMethod = "portTimeoutFallBack") public Integer portTimeout() { 
    String url = "http://user-demo/user/port"; URI uri = UriComponentsBuilder.fromHttpUrl(url).build().toUri(); return restTemplate.getForObject(uri, Integer.class); } // 当 portTimeout 调用出现超时的时候;会返回该方法的返回值 public Integer portTimeoutFallBack() { 
    return -1; } 

Open Fegin 远程调用

Open Fegin = RestTemplate + Ribbon + Hystrix

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-openfeign 
      artifactId>  
       dependency> 

启动类要添加 @EnableFeignClients 注解

@FeignClient(name = "user-demo",fallback = UserApiFallback.class,path = "/user") public interface UserApi { 
    //调⽤的请求路径 @RequestMapping(value = "/port") Integer port(); } 

打开 Open Fegin 对于 Hystrix 熔断的支持

application.yml

feign: hystrix: enabled: true 
@Component public class UserApiFallback implements UserApi { 
    @Override public Integer port() { 
    return -2; } } 

远程调用

@Autowired private UserApi userApi; public Integer portTimeout() { 
    return userApi.port(); } 

GateWay 网关

Spring Cloud 网关需要 Spring Boot 和 Spring Webflux 提供的 Netty 运行时。它不能在传统的 Servlet 容器中或作为 WAR 构建。

pom.xml

<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-gateway 
         artifactId>  
          dependency> 

application.yml

server: port: 9200 spring: application: name: gateway-server cloud: gateway: routes: # 路由id 保持唯一 - id: user-route # 负载均衡的地址 uri: lb://user-demo # 根据请求路径进行过滤 predicates: - Path=/user/ - id: gift-route uri: lb://gift-demo predicates: - Path=/gift/ eureka: client: service-url: defaultZone: http://eureka-server-01:8701/eureka,http://eureka-server-02:8702/eureka 

全局过滤器

@Component public class GlobalFilterConfig implements GlobalFilter, Ordered { 
    @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { 
    ServerHttpRequest request = exchange.getRequest(); String host = request.getRemoteAddress().getHostString(); System.out.println(host); return chain.filter(exchange); } @Override public int getOrder() { 
    // 返回值表示当前过滤器的顺序(优先级),数值越⼩,优先级越⾼ return 0; } } 

Spring Cloud Config 配置中心

Config Server 服务端

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-netflix-eureka-client 
      artifactId>  
       dependency> <dependency> <groupId>org.springframework.cloud 
        groupId> <artifactId>spring-cloud-config-server 
         artifactId>  
          dependency> 

启动类需要添加 @EnableConfigServer 注解

application.yml

server: port: 9600 spring: application: name: config-server cloud: config: server: git: # git 的信息 uri: https://gitee.com/zhangyizhou/learning-config-server.git username: root password:  # 分支 label: master eureka: client: service-url: defaultZone: http://eureka-server-01:8701/eureka,http://eureka-server-02:8702/eureka 

Config Server 客户端

将 application.yml 改为 bootstrap.yml

bootstrap.yml

spring: cloud: config: name: local-config uri: http://127.0.0.1:9600 
配置文件更新后 手动刷新
  1. 在使用到配置文件的类上添加 @RefreshScope 注解
  2. 添加 actuator 依赖
  3. post 方式调用 http://127.0.0.1:8092/actuator/refresh 接口

pom.xml

<dependency> <groupId>org.springframework.boot 
     groupId> <artifactId>spring-boot-starter-actuator 
      artifactId>  
       dependency> 

bootstrap.yml

management: endpoints: web: exposure: include: "*" 

Sleuth + Zipkin 分布式链路追踪

Sleuth

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-zipkin 
      artifactId>  
       dependency> 

调整日志等级

application.yml

logging: level: org.springframework.web.servlet.DispatcherServlet: debug org.springframework.cloud.sleuth: debug 

Zipkin Server 服务端

pom.xml

<dependency> <groupId>io.zipkin.java 
     groupId> <artifactId>zipkin-server 
      artifactId>  
      <exclusions> <exclusion> <groupId>org.springframework.boot 
       groupId> <artifactId>spring-boot-starter-log4j2 
        artifactId>  
         exclusion>  
          exclusions> <version>2.12.3 
           version>  
            dependency> <dependency> <groupId>io.zipkin.java 
             groupId> <artifactId>zipkin-autoconfigure-ui 
              artifactId> <version>2.12.3 
               version>  
                dependency> 

启动类需要 @EnableZipkinServer 注解

server: port: 9400 management: metrics: web: server: # 需要设置为 false auto-time-requests: false 

Zipkin Client 客户端

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-zipkin 
      artifactId>  
       dependency> 

application.yml

spring: zipkin: # zipkin server 的地址 base-url: http://127.0.0.1:9400 sender: # 通过什么方式发送到 server 端 type: web sleuth: sampler: # 日志的采样率;默认为 0.1 probability: 1 

Zipkin Server 链路持久化到 MySQL

pom.xml

<dependency> <groupId>io.zipkin.java 
     groupId> <artifactId>zipkin-autoconfigure-storage-mysql 
      artifactId> <version>2.12.3 
       version>  
        dependency> <dependency> <groupId>mysql 
         groupId> <artifactId>mysql-connector-java 
          artifactId>  
           dependency> <dependency> <groupId>org.springframework 
            groupId> <artifactId>spring-jdbc 
             artifactId>  
              dependency> 

application.yml

spring: datasource: url: jdbc:mysql://localhost:3306/zipkin?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC username: root password:  zipkin: storage: # 指定存储方式 type: mysql 
CREATE TABLE IF NOT EXISTS zipkin_spans ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL, `id` BIGINT NOT NULL, `name` VARCHAR(255) NOT NULL, `remote_service_name` VARCHAR(255), `parent_id` BIGINT, `debug` BIT(1), `start_ts` BIGINT COMMENT 'Span.timestamp(): epoch micros used for endTs query and to implement TTL', `duration` BIGINT COMMENT 'Span.duration(): micros used for minDuration and maxDuration query', PRIMARY KEY (`trace_id_high`, `trace_id`, `id`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_spans ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTracesByIds'; ALTER TABLE zipkin_spans ADD INDEX(`name`) COMMENT 'for getTraces and getSpanNames'; ALTER TABLE zipkin_spans ADD INDEX(`remote_service_name`) COMMENT 'for getTraces and getRemoteServiceNames'; ALTER TABLE zipkin_spans ADD INDEX(`start_ts`) COMMENT 'for getTraces ordering and range'; CREATE TABLE IF NOT EXISTS zipkin_annotations ( `trace_id_high` BIGINT NOT NULL DEFAULT 0 COMMENT 'If non zero, this means the trace uses 128 bit traceIds instead of 64 bit', `trace_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.trace_id', `span_id` BIGINT NOT NULL COMMENT 'coincides with zipkin_spans.id', `a_key` VARCHAR(255) NOT NULL COMMENT 'BinaryAnnotation.key or Annotation.value if type == -1', `a_value` BLOB COMMENT 'BinaryAnnotation.value(), which must be smaller than 64KB', `a_type` INT NOT NULL COMMENT 'BinaryAnnotation.type() or -1 if Annotation', `a_timestamp` BIGINT COMMENT 'Used to implement TTL; Annotation.timestamp or zipkin_spans.timestamp', `endpoint_ipv4` INT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_ipv6` BINARY(16) COMMENT 'Null when Binary/Annotation.endpoint is null, or no IPv6 address', `endpoint_port` SMALLINT COMMENT 'Null when Binary/Annotation.endpoint is null', `endpoint_service_name` VARCHAR(255) COMMENT 'Null when Binary/Annotation.endpoint is null' ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; ALTER TABLE zipkin_annotations ADD UNIQUE KEY(`trace_id_high`, `trace_id`, `span_id`, `a_key`, `a_timestamp`) COMMENT 'Ignore insert on duplicate'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`, `span_id`) COMMENT 'for joining with zipkin_spans'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id_high`, `trace_id`) COMMENT 'for getTraces/ByIds'; ALTER TABLE zipkin_annotations ADD INDEX(`endpoint_service_name`) COMMENT 'for getTraces and getServiceNames'; ALTER TABLE zipkin_annotations ADD INDEX(`a_type`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`a_key`) COMMENT 'for getTraces and autocomplete values'; ALTER TABLE zipkin_annotations ADD INDEX(`trace_id`, `span_id`, `a_key`) COMMENT 'for dependencies job'; CREATE TABLE IF NOT EXISTS zipkin_dependencies ( `day` DATE NOT NULL, `parent` VARCHAR(255) NOT NULL, `child` VARCHAR(255) NOT NULL, `call_count` BIGINT, `error_count` BIGINT, PRIMARY KEY (`day`, `parent`, `child`) ) ENGINE=InnoDB ROW_FORMAT=COMPRESSED CHARACTER SET=utf8 COLLATE utf8_general_ci; 

Spring Cloud OAuh2 微服务统一认证

Authorization Server 认证服务器

pom.xml

<dependencies> <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-oauth2 
         artifactId> <exclusions> <exclusion> <groupId>org.springframework.security.oauth.boot 
          groupId> <artifactId>spring-security-oauth2-autoconfigure 
           artifactId>  
            exclusion>  
             exclusions>  
              dependency> <dependency> <groupId>org.springframework.security.oauth.boot 
               groupId> <artifactId>spring-security-oauth2-autoconfigure 
                artifactId> <version>2.1.11.RELEASE 
                 version>  
                  dependency>  
                   dependencies> 

application.yml

server: port: 9999 spring: application: name: oauth-server eureka: client: service-url: defaultZone: http://eureka-server-01:8701/eureka,http://eureka-server-02:8702/eureka 
@Configuration @EnableAuthorizationServer public class OauthServerConfig extends AuthorizationServerConfigurerAdapter { 
    @Autowired private AuthenticationManager authenticationManager; @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 
    // 允许客户端表单认证 security.allowFormAuthenticationForClients() // 开启端口 /oauth/token_key 的访问权限(允许) .tokenKeyAccess("permitAll()") // 开启端口 /oauth/check_token 的访问权限(允许) .checkTokenAccess("permitAll()"); } // 客户端详情配置 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 
    // 客户端信息存储在什么地方 clients.inMemory() // 添加 client 的配置 .withClient("admin").secret("") // 指定客户端能够访问的资源 id 清单 需要和资源服务器上配置的一致 .resourceIds("gift") // 认证类型/令牌颁发模式 .authorizedGrantTypes("password", "refresh_token") // 客户端的权限范围 .scopes("all"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 
    // 指定 token 的存储方式 endpoints.tokenStore(tokenStore()) // token 生成的细节 .tokenServices(authorizationServerTokenServices()) // 指定认证管理器 .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET); } public TokenStore tokenStore() { 
    return new InMemoryTokenStore(); } public AuthorizationServerTokenServices authorizationServerTokenServices() { 
    DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); // 是否开启令牌刷新 defaultTokenServices.setSupportRefreshToken(true); defaultTokenServices.setTokenStore(tokenStore()); // 设置令牌的有效时间 defaultTokenServices.setAccessTokenValiditySeconds(20); // 设置刷新令牌的有效时间 defaultTokenServices.setRefreshTokenValiditySeconds(3600 * 24 * 3); return defaultTokenServices; } } 
@Configuration public class SecurityConfig extends WebSecurityConfigurerAdapter { 
    @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { 
    return super.authenticationManagerBean(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    UserDetails user = new User("root", "", new ArrayList<>()); auth.inMemoryAuthentication().withUser(user).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { 
    return NoOpPasswordEncoder.getInstance(); } } 

Resource Server 资源服务器

pom.xml

<dependency> <groupId>org.springframework.cloud 
     groupId> <artifactId>spring-cloud-starter-oauth2 
      artifactId> <exclusions> <exclusion> <groupId>org.springframework.security.oauth.boot 
       groupId> <artifactId>spring-security-oauth2-autoconfigure 
        artifactId>  
         exclusion>  
          exclusions>  
           dependency> <dependency> <groupId>org.springframework.security.oauth.boot 
            groupId> <artifactId>spring-security-oauth2-autoconfigure 
             artifactId> <version>2.1.11.RELEASE 
              version>  
               dependency> 
@Configuration @EnableResourceServer @EnableWebSecurity public class ResourceServerConfig extends ResourceServerConfigurerAdapter { 
    @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 
    resources.resourceId("gift"); RemoteTokenServices remoteTokenServices = new RemoteTokenServices(); // 认证服务器的地址 remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token"); remoteTokenServices.setClientId("admin"); remoteTokenServices.setClientSecret(""); resources.tokenServices(remoteTokenServices); } @Override public void configure(HttpSecurity http) throws Exception { 
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and().authorizeRequests() .antMatchers("/gift/").authenticated() .antMatchers("/user/").authenticated() .anyRequest().permitAll(); } } 

JWT 引入

RemoteTokenServices 远程请求授权服务验证 token,如果访问量较⼤将会影响系统的性能。

认证服务器改造
private final String sign_key = "sign_key"; public TokenStore tokenStore() { 
    return new JwtTokenStore(jwtAccessTokenConverter()); } public JwtAccessTokenConverter jwtAccessTokenConverter() { 
    JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setSigningKey(sign_key); // 签名密钥 jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的密钥,和签名密钥保持一致 return jwtAccessTokenConverter; } public AuthorizationServerTokenServices authorizationServerTokenServices() { 
    // ... defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter()); // ... } 
资源服务器改造
public void configure(ResourceServerSecurityConfigurer resources) throws Exception { 
    resources.resourceId("gift").tokenStore(tokenStore()).stateless(true); } 

从数据库读取信息

客户端详情配置

pom.xml

<dependency> <groupId>mysql 
     groupId> <artifactId>mysql-connector-java 
      artifactId>  
       dependency> <dependency> <groupId>org.springframework.boot 
        groupId> <artifactId>spring-boot-starter-data-jpa 
         artifactId>  
          dependency> 

application.yml

spring: datasource: url: jdbc:mysql:///bank?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC username: root password:  

oauth_client_details

CREATE TABLE `oauth_client_details` ( `client_id` varchar(48) NOT NULL, `resource_ids` varchar(256) DEFAULT NULL, `client_secret` varchar(256) DEFAULT NULL, `scope` varchar(256) DEFAULT NULL, `authorized_grant_types` varchar(256) DEFAULT NULL, `web_server_redirect_uri` varchar(256) DEFAULT NULL, `authorities` varchar(256) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(4096) DEFAULT NULL, `autoapprove` varchar(256) DEFAULT NULL, PRIMARY KEY (`client_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 
// 客户端详情配置 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 
    clients.withClientDetails(jdbcClientDetailsService()); } @Autowired private DataSource dataSource; @Bean public JdbcClientDetailsService jdbcClientDetailsService() { 
    return new JdbcClientDetailsService(dataSource); } 
客户端认证配置
public interface PayingUserDao extends JpaRepository<PayingUser, Integer> { 
   } 
@Service public class PayingUserService implements UserDetailsService { 
    @Autowired private PayingUserDao payingUserDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
    // 基于数据库进行验证 PayingUser payingUser = new PayingUser(); payingUser.setUsername(username); Example<PayingUser> payingUserExample = Example.of(payingUser); Optional<PayingUser> payingUserOptional = payingUserDao.findOne(payingUserExample); if (payingUserOptional.isEmpty()) { 
    // 用户名没有找到 System.out.println(username + "没有找到..."); throw new UsernameNotFoundException(username); } PayingUser user = payingUserOptional.get(); // 权限集合 Collection<GrantedAuthority> authorities = new ArrayList<>(); return new User(user.getUsername(), user.getPassword(), authorities); } } 
@Autowired private PayingUserService payingUserService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { 
    auth.userDetailsService(payingUserService).passwordEncoder(passwordEncoder()); } 

自定义 JWT 数据

认证服务器添加数据
@Component public class CustomAccessTokenConverter extends DefaultAccessTokenConverter { 
    @Override public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { 
    ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); String remoteAddr = requestAttributes.getRequest().getRemoteAddr(); Map<String, String> map = (Map<String, String>) super.convertAccessToken(token, authentication); map.put("clientIp", remoteAddr); return map; } } 
资源服务器取出数据
@Component public class CustomAccessTokenConverter extends DefaultAccessTokenConverter { 
    @Override public OAuth2Authentication extractAuthentication(Map<String, ?> map) { 
    OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map); oAuth2Authentication.setDetails(map); return oAuth2Authentication; } } 
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails(); Map<String,Object> decodedDetails = (Map<String, Object>) details.getDecodedDetails(); System.out.println(decodedDetails.get("clientIp")); 

参考git:https://gitee.com/zhangyizhou/learning-spring-cloud-greenwich-demo.git

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/210846.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月18日 下午11:47
下一篇 2026年3月18日 下午11:47


相关推荐

  • 大津阈值法MATLAB实现

    大津阈值法MATLAB实现关于大津阈值法的定义这里不再赘述 直接给出实现代码 image graythresh mfunctionlev image graythresh I 该函数作用 使用大津阈值法找出图像分割的阈值 M N size I 预定义划分的两组 分别为 C1 C2 预定义 level 用于存放不同划分时的类间方差 C1 zeros 1 M N C2 ze

    2026年3月26日
    1
  • BSON类型

    BSON类型目录 1ObjectId2 字符串 3 时间戳 4 日期 BSON 是一种二进制序列化格式 用于在 MongoDB 中存储文档和进行远程过程调用 BSON 规范位于 bsonspec org 每种 BSON 类型都有数字和字符串作为标识符 如下表所示 类型数字别名备注 Double1 double String2 string Object3 object

    2026年3月26日
    2
  • 面向对象编程与面向过程编程的基本区别

    面向对象编程与面向过程编程的基本区别面向对象 面向对象编程 ObjectOrient 面向对象程序设计 的主要思想是把构成问题的各个事务分解成各个对象 建立对象的目的不是为了完成一个步骤 而是为了描叙一个事物在整个解决问题的步骤中的行为 面向对象程序设计中的概念主要包括 对象 类 数据抽象 继承 动态绑定 数据封装 多态性 消息传递 通过这些概念面向对象的思想得到了具体的体现 面向对象是一种解决问题的思想类就是具备某些共同特征的实体的集合 它是一种抽象的数据类型 它是对所具有相同特征实体的抽象 在面向对象的程序

    2026年3月20日
    3
  • 跨域与跨域访问_如何实现跨域访问

    跨域与跨域访问_如何实现跨域访问什么是跨域跨域是指从一个域名的网页去请求另一个域名的资源。比如从www.baidu.com页面去请求www.google.com的资源。跨域的严格一点的定义是:只要协议,域名,端口有任何一个的不同,就被当作是跨域为什么浏览器要限制跨域访问呢?原因就是安全问题:如果一个网页可以随意地访问另外一个网站的资源,那么就有可能在客户完全不知情的情况下出现安全问题。比如下面的操作就有安全问题

    2022年10月1日
    4
  • 协方差、协方差矩阵的计算实例

    协方差、协方差矩阵的计算实例一 协方差的计算已知两组数据 计算其协方差 X Y Cov X Y E XY E X E Y E X 6 5 4 12 9 5 6 6E Y 5 15 5 6 7 5 7 6E XY 3 5 5 15 4 5 12 6 9 7 5 49Cov

    2026年3月18日
    2
  • PotPlayer的官网「建议收藏」

    PotPlayer的官网「建议收藏」访问PotPlayer的官网,需要先能访问到外面的网络。http://potplayer.tv/http://potplayer.daum.net/如果无法访问外面的网络,可以使用下面的阿里云盘链接阿里云盘分享

    2022年7月27日
    27

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号