从0手写springCloud项目(模块搭建)

写在前面

一直在写springCloud项目,每次都是新建项目然后从零开始写配置,现在写一个尽量通用的项目,方便后续搭建框架的时候直接拿过去使用。

  1. 需要搭建的组件(模块)有:
    eureka(认证),zuul(网关),auth(认证),config(配置中心),user(用户),order(订单),pay(支付),feign...
  2. 这边主要想涉及到的框架技术有:springcloud,springboot2,oauth2,springSecurity,liquibase,lcn(5.0.2),mybatisplus,logback,redis,mysql,swagger2,poi
  3. 需要搭建、支持的技术
    github,jenkins(自动发布),maven私服,nginx,redis,mysql5.7,jdk1.8,swagger2,rabbitmq
一 需要搭建的组件

需要搭建的组件主要有7个模块(feign会集成到具体模块),这边我回详细记录eureka,zuul,auth,config,user.因为前四者是springCloud的配置。需要详细介绍,而具体的业务逻辑代码会在具体模块,这里我将以user模块为例子详细介绍.

  • eureka

我们知道,在为服务里面,所有模块需要被注册到一个注册中心,持续的向注册中心发送心跳以保证连接的存活。而springcloud的注册中心有consul和eureka,这里我选用的是eureka.
eureka的代码很简单,只需要在配置文件里面配置好注册地址与密码(可不设置,生产上强烈建议设置),并标识好自己不向自己注册,不被自己发现即可。

maven坐标:

 
    
        org.springframework.boot
        spring-boot-starter-web
    
    
    
        org.springframework.cloud
        spring-cloud-starter-netflix-eureka-server
    
    
        org.springframework.boot
        spring-boot-starter-test
        test
    

主类,不用做任何配置

@SpringBootApplication
@EnableEurekaServer
public class CrawlerEurekaApplication {

    public static void main(String[] args) {
        SpringApplication.run(CrawlerEurekaApplication.class, args);
    }
}


yml配置文件:

spring:
  application:
    name: crawler-eureka

server:
  host: http://localhost
  port: 9990
eureka:
  client:
    fetch-registry: false
    register-with-eureka: false
    service-url:
      defaultZone: ${server.host}:${server.port}/eureka/
  instance:
    prefer-ip-address: true
  • zuul

上面我们把注册中心搭建好了,访问localhost:9990就可以看到eureka的控制台。但是我们看不到一个服务注册上去了。现在我们搭建一个网关,因为在实际项目中,我们会有很多个微服务模块,而服务器只会向外暴露一个端口,其他的通过相对路径转发。这样也是为了安全和方便管理,有点nginx的感觉。
网关的配置也不复杂:pom坐标:


        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
        
        
            org.springframework.cloud
            spring-cloud-starter-netflix-zuul
        
    

    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

主类除了标识为eureka-client,还标识是网关

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

yml配置

server:
  port: 9996
spring:
  application:
    name: crawler-zuul
  redis:
    host: localhost
    port: 6379
    password: 123456

zuul:
  routes:
    feign-auth:
      path: /auth/**
      serviceId: crawler-auth
      strip-prefix: true
      custom-sensitive-headers: true
    feign-user:
      path: /user/**
      serviceId: crawler-goddess
      sensitiveHeaders:

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:9990/eureka/
  instance:
    prefer-ip-address: true

logging:
  level:
    ROOT: info
    org.springframework.web: info

ribbon:
  ReadTimeout: 6000000
  SocketTimeout: 6000000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 600000

启动项目,再次打开localhost:9990可以发现多了一个crawler-zuul

  • auth
    auth模块主要是供其他模块接口进入时提供认证。这里使用的是oauth2.3.3和springSecurity5.1.5(security5与之前的版本略有区别,接下来会介绍到)
    pom:

    
    
       
       
           cn.iamcrawler
           crawler_common
           1.0.1
       
    
       
           org.springframework.boot
           spring-boot-starter-test
           test
       
    
       
           org.springframework.cloud
           spring-cloud-starter-netflix-eureka-client
           2.1.0.RELEASE
       
    
       
           org.springframework.cloud
           spring-cloud-starter-openfeign
           2.1.0.RELEASE
       
    
       
           org.projectlombok
           lombok
           true
       
       
           org.springframework.boot
           spring-boot-starter-actuator
       
    
       
           org.springframework.cloud
           spring-cloud-starter-security
           
               
               
                   spring-security-oauth2
                   org.springframework.security.oauth
               
           
       
    
    
    
     
           org.springframework.security.oauth
           spring-security-oauth2
           2.3.3.RELEASE
    
    
    
    

    yml:

     server:
         port: 9992
     spring:
         datasource:
           url: jdbc:mysql://www.iamcrawler.cn:3306/goddess?characterEncoding=utf8&useUnicode=true&useSSL=false&serverTimezone=UTC
           username: root
           password: 123456
           driver-class-name:  com.mysql.jdbc.Driver
         redis:
           host: www.iamcrawler.cn
           port: 6379
           password:
           database: 0
         application:
           name: crawler-auth
       eureka:
         client:
           service-url:
             defaultZone: http://localhost:9990/eureka/
       management:
         endpoints:
           web:
             exposure:
               include: health,info,loggers
    

下面介绍一下主要的两个配置类(sercurity+oauth2)和重写的userDetailService,一般其他人的教程里会把oauth2的配置又分为资源服务器和认证服务器两个,我这里写在同一个配置里面了,道理都是一样的。

    /**
     * Created by liuliang
     * on 2019/6/19
     */

@Configuration
@EnableWebSecurity
//开启权限认证
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private DomainUserDetailsService userDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return Objects.equals(charSequence.toString(),s);
            }
        };
    }

    /**
     * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http
                .requestMatchers().anyRequest()
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/*").permitAll()
        ;
    }
}

----------------类分割线----------------------------

  
/**
 * Created by liuliang
 * on 2019/6/19
 */

@Configuration
public class OAuth2ServerConfig {

    private static final String DEMO_RESOURCE_ID = "text";

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(DEMO_RESOURCE_ID).stateless(true);
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.headers().frameOptions().disable();
            http
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .authorizeRequests()
                    .antMatchers("/loggers/**").permitAll()
                    .and()
                    .authorizeRequests()
                    .antMatchers("/templates/**").permitAll()
            ;

        }
    }


    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {

        @Autowired
        AuthenticationManager authenticationManager;
        @Autowired
        RedisConnectionFactory redisConnectionFactory;


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //配置两个客户端,一个用于password认证一个用于client认证
            clients.inMemory().withClient("client_1")
                    .resourceIds(DEMO_RESOURCE_ID)
                    .authorizedGrantTypes("client_credentials", "refresh_token")
                    .scopes("select")
                    .authorities("client")
                    .secret("{noop}1234567")
                    .and().withClient("client_2")
                    .resourceIds(DEMO_RESOURCE_ID)
                    .authorizedGrantTypes("password", "refresh_token")
                    .scopes("select")
                    .authorities("client")
                    .secret("{noop}1234567");
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .tokenStore(new RedisTokenStore(redisConnectionFactory))
                    .authenticationManager(authenticationManager);
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            //允许表单认证
            oauthServer.allowFormAuthenticationForClients();
        }

    }

}

----------------类分割线----------------------------

  /**
 * Created by liuliang
 * on 2019/6/18
 */
@Component
@Slf4j
public class DomainUserDetailsService implements UserDetailsService {

    @Resource
    private GoddessUserService userService ;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        GoddessUser goddessUser = userService.getOne(new QueryWrapper().eq("user_name", s));
        log.info("user:"+goddessUser.getUsername()+" login success!");
        return goddessUser;
    }
}

到这里主要的配置就写完了,但是这脸我想多说明几句。因为之前使用的是springboot1.5.9版本+springSecurity4的版本集成oauth2,现在这次版本升级,使用的是springboot2.1.3+springSecurity5。在代码没有任何变化的时候,获取token总是会抛出各种认证失败的问题,比如“Encoded password does not look like BCrypt”,“id is null”等问题,百度很久都说明原因,说security5添加了密码加密,但是解答的部分都很水,最后我通过debug源码的方式,发现有一个client_secret 的始终为空,原来需要在请求的时候,将这个绑定进去:如下截图

从0手写springCloud项目(模块搭建)_第1张图片

这个需要与OAuth2ServerConfig类配置文件里面认证服务类AuthorizationServerConfiguration 的 clients.inMemory()... .secret("{noop}1234567")对应,他是从client这里取的。
ps:我这里为了测试简单,没有将密码加密,即使用的是{noop}模式,建议生产上改一下,加密会比较安全...

好了,现在访问如上截图的接口,会返回如下:

{
    "access_token": "1c72d16d-0647-4b57-b63a-eb4e4a197804",
    "token_type": "bearer",
    "refresh_token": "09eddee3-5ce9-4904-8c23-acfe738579c9",
    "expires_in": 43198,
    "scope": "select"
}

至此,auth模块发布成功

  • config

待写
(这个模块等我把后续文章写完了,回过头来补起来。下一篇文章主要介绍user模块框架涉及以及涉及的主要技术点)

你可能感兴趣的:(从0手写springCloud项目(模块搭建))