美文网首页SpringBoot精选架构设计微服务
微服务开发基础框架学习与使用

微服务开发基础框架学习与使用

作者: 卡普_2224 | 来源:发表于2019-01-17 11:32 被阅读29次

    在此感谢原作者的分享。https://blog.csdn.net/w1054993544/article/details/78932614

    1.架构图

    技术团队通过一段时间的积累后,我们打算对往后的一些新项目采用Spring Cloud技术栈来实现。大概微服务的架构如下:


    1.框架图.png
    2.项目工程结构图.png
    3操作流程图.png

    2.Eureka服务注册与发现

    •服务注册中心:服务的注册与发现。


    4.Euraka注册中心集群.png
    5.和doubble对比.png
    6.提供服务和注册服务.png

    3.OAUTH2认证服务器

    我这里采用Security-OAuth2.0 密码模式实现


    7.oauth2流程图.png

    3.1 oauth2 server 配置

    我采取了数据库和redis两种方式来存储token,可以方便切换,生成环境下建议使用redis方式。

    3.1.1AuthorizationServerConfig

    package com.microservice.skeleton.auth.config;
    
    import com.microservice.skeleton.auth.error.MssWebResponseExceptionTranslator;
    import com.microservice.skeleton.auth.service.impl.UserDetailsServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.http.HttpMethod;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
    import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
    import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
    import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
    import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
    import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
    import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
    
    import javax.sql.DataSource;
    
    /**
     * Created by karp
     * Time:11:02
     * ProjectName:Mirco-Service-Skeleton
     */
    @Configuration
    @EnableAuthorizationServer
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    
        @Autowired
        private AuthenticationManager authenticationManager;
    
        @Autowired
        private DataSource dataSource;
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
    
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        @Bean
        RedisTokenStore redisTokenStore(){
            return new RedisTokenStore(redisConnectionFactory);
        }
    
        //token存储数据库
    //    @Bean
    //    public JdbcTokenStore jdbcTokenStore(){
    //        return new JdbcTokenStore(dataSource);
    //    }
    
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.withClientDetails(clientDetails());
        }
        @Bean
        public ClientDetailsService clientDetails() {
            return new JdbcClientDetailsService(dataSource);
        }
        @Bean
        public WebResponseExceptionTranslator<OAuth2Exception> webResponseExceptionTranslator(){
            return new MssWebResponseExceptionTranslator();
        }
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    //        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
            endpoints.tokenStore(redisTokenStore())
                    .userDetailsService(userDetailsService)
                    .authenticationManager(authenticationManager);
            endpoints.tokenServices(defaultTokenServices());
            endpoints.exceptionTranslator(webResponseExceptionTranslator());//认证异常翻译
        }
    
        /**
         * <p>注意,自定义TokenServices的时候,需要设置@Primary,否则报错,</p>
         * @return
         */
        @Primary
        @Bean
        public DefaultTokenServices defaultTokenServices(){
            DefaultTokenServices tokenServices = new DefaultTokenServices();
            tokenServices.setTokenStore(redisTokenStore());
            tokenServices.setSupportRefreshToken(true);
            tokenServices.setClientDetailsService(clientDetails());
            tokenServices.setAccessTokenValiditySeconds(60*60*12); // token有效期自定义设置,默认12小时
            tokenServices.setRefreshTokenValiditySeconds(60 * 60 * 24 * 7);//默认30天,这里修改
            return tokenServices;
        }
    
        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.tokenKeyAccess("permitAll()");
            security .checkTokenAccess("isAuthenticated()");
            security.allowFormAuthenticationForClients();
        }
    }
    
    

    3.1.2WebSecurityConfig:

    package com.microservice.skeleton.auth.config;
    
    import com.microservice.skeleton.auth.service.impl.UserDetailsServiceImpl;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.security.authentication.AuthenticationManager;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    
    /**
     * Created by karp
     * Time:16:42
     * ProjectName:Mirco-Service-Skeleton
     */
    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        @Autowired
        private UserDetailsServiceImpl userDetailsService;
    
        @Bean
        public PasswordEncoder passwordEncoder() {
            return new BCryptPasswordEncoder();
        }
    
        @Override
        @Bean
        public AuthenticationManager authenticationManagerBean() throws Exception {
            return super.authenticationManagerBean();
        }
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder());
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                    .authorizeRequests()
                    .anyRequest().fullyAuthenticated()
                    .antMatchers("/oauth/token").permitAll()
                    .and()
                    .csrf().disable();
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring().antMatchers("/css/**", "/js/**", "/plugins/**", "/favicon.ico");
        }
    
    }
    
    

    permitAll() 很关键:其中antMatchers("/oauth/token").permitAll() 给获取token请求授权。

    3.2 UserDetailsServiceImpl实现

    package com.microservice.skeleton.auth.service.impl;
    import ch.qos.logback.core.net.SyslogOutputStream;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.microservice.skeleton.auth.service.PermissionService;
    import com.microservice.skeleton.auth.service.RoleService;
    import com.microservice.skeleton.auth.service.UserService;
    import com.microservice.skeleton.common.util.StatusCode;
    import com.microservice.skeleton.common.vo.MenuVo;
    import com.microservice.skeleton.common.vo.Result;
    import com.microservice.skeleton.common.vo.RoleVo;
    import com.microservice.skeleton.common.vo.UserVo;
    import org.springframework.beans.BeanUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.security.core.authority.SimpleGrantedAuthority;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.stereotype.Service;
    
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    /**
     * Created by karp
     * Time:16:37
     * ProjectName:Mirco-Service-Skeleton
     */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
        @Autowired
        private UserService userService;
        @Autowired
        private RoleService roleService;
        @Autowired
        private PermissionService permissionService;
    
        private ObjectMapper objectMapper = new ObjectMapper();
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            Result<UserVo> userResult = userService.findByUsername(username);
            if (userResult.getCode() != StatusCode.SUCCESS_CODE) {
                throw new UsernameNotFoundException("用户:" + username + ",不存在!");
            }
            Set<GrantedAuthority> grantedAuthorities = new HashSet<>();
            boolean enabled = true; // 可用性 :true:可用 false:不可用
            boolean accountNonExpired = true; // 过期性 :true:没过期 false:过期
            boolean credentialsNonExpired = true; // 有效性 :true:凭证有效 false:凭证无效
            boolean accountNonLocked = true; // 锁定性 :true:未锁定 false:已锁定
            UserVo userVo = new UserVo();
            BeanUtils.copyProperties(userResult.getData(),userVo);
            Result<List<RoleVo>> roleResult = roleService.getRoleByUserId(userVo.getId());
            if (roleResult.getCode() == StatusCode.SUCCESS_CODE){
                List<RoleVo> roleVoList = roleResult.getData();
                for (RoleVo role:roleVoList){
                    //角色必须是ROLE_开头,可以在数据库中设置
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_"+role.getValue());
                    System.out.println("grantedAuthority---角色------->ROLE_"+role.getValue());
                    grantedAuthorities.add(grantedAuthority);
                    //获取权限
                    Result<List<MenuVo>> perResult  = permissionService.getRolePermission(role.getId());
                    if (perResult.getCode() == StatusCode.SUCCESS_CODE){
                        List<MenuVo> permissionList = perResult.getData();
                        for (MenuVo menu:permissionList
                                ) {
                            GrantedAuthority authority = new SimpleGrantedAuthority(menu.getUrl());
                            grantedAuthorities.add(authority);
                            System.out.println("grantedAuthority---菜单------->"+menu.getUrl());
                        }
                    }
                }
            }
            User user = new User(userVo.getUsername(), userVo.getPassword(),
                    enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, grantedAuthorities);
            return user;
        }
    }
    
    

    3.3 mss-oauth yml文件配置

    server:
      port: 9060
    
    spring:
      application:
        name: authcenter
      jpa:
        show-sql: true
      datasource:
        url: jdbc:mysql://localhost:4306/nsinformation?useUnicode=true&characterEncoding=utf-8
        username: nsinformation
        password: ***
        druid:
          driver-class-name: com.mysql.jdbc.Driver
      redis:
        host: ***
        port: 6489
        password: ***
    eureka:
      instance:
        prefer-ip-address: true
        instance-id: ${spring.cloud.client.ip-address}:${server.port}
        ##续约更新时间间隔设置5秒,m默认30s
        lease-renewal-interval-in-seconds: 1
        ##续约到期时间10秒,默认是90秒
        lease-expiration-duration-in-seconds: 2
      client:
          service-url:
            defaultZone: http://mss-eureka:9010/eureka/ #目前是单节点,多节点可以在后面追加
    logging:
      config: classpath:logback.xml
      level:
        org:
          springframework:
            web: info
    ###feign 默认关闭熔断,请看HystrixFeignConfiguration
    feign:
      hystrix:
        enabled: false
    hystrix:
      command:
        default:
          execution:
            isolation:
              thread:
                timeoutInMilliseconds: 60000
    ribbon:
      ReadTimeout: 30000
      ConnectTimeout: 60000
      MaxAutoRetries: 0
      MaxAutoRetriesNextServer: 1
      ActiveConnectionsLimit: 2147483647
    

    4.mss-config配置中心

    4.1配置中心serve端

    server.port = 8001
    server.tomcat.uri-encoding = UTF-8
    
    spring.application.name=configServer
    spring.profiles.active=subversion
    spring.cloud.config.server.svn.uri=http://***:1080/AgriculturalWaterInformatization/trunk/Micro-Service-Skeleton/configfiletest/
    spring.cloud.config.server.svn.username=***
    spring.cloud.config.server.svn.password=***
    spring.cloud.config.server.default-label={application}
    spring.cloud.config.enabled=true
    

    4.2配置中心客户端

    8.image.png

    5.Zuul网关(mss-gateway)

    5.1开启支持Sso

    @Configuration
    @EnableOAuth2Sso
    public class SecurityConfig extends WebSecurityConfigurerAdapter{
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable();
        }
    }
    
    

    5.2配置文件

    网关把/uaa/**请求都转发到oauth2.0中心,oauth2.0中心对所有的请求需要认证授权,如果不需要授权,那么在WebSecurityConfig设置permitall就可以了。

    spring:
      application:
        name: mss-gateway
    server:
      port: 9030
    eureka:
      instance:
        prefer-ip-address: true
        instance-id: ${spring.cloud.client.ip-address}:${server.port}
        lease-renewal-interval-in-seconds: 1 # 心跳检测检测与续约时间
        lease-expiration-duration-in-seconds: 2 # 测试时将值设置设置小些,保证服务关闭后注册中心能及时踢出服
      client:
          service-url:
            defaultZone: http://mss-eureka:9010/eureka/
    zuul:
      host:
        connect-timeout-millis: 10000
        socket-timeout-millis: 60000
      routes:
        uaa:
          path: /uaa/**
          strip-prefix: true
          sensitiveHeaders:
          serviceId: authcenter
        upms:
          path: /upms/**
          strip-prefix: true
          sensitiveHeaders:
          serviceId: mss-upms
    management:
      security:
        enabled: false
    security:
      oauth2:
        client:
          access-token-uri: http://localhost:9030/uaa/oauth/token #网关的地址
          user-authorization-uri: http://localhost:9030/uaa/oauth/authorize
        resource:
          user-info-uri: http://localhost:9060/user #安全策略里获取用户信息。
          prefer-token-info: false
    
    #    resource:
    #      user-info-uri:  http://localhost:9060/upms/user
    #      prefer-token-info: false
    ##############end#####################
    ####超时配置####
    ribbon:
      ReadTimeout: 20000
      ConnectTimeout: 20000
      MaxAutoRetries: 1
      MaxAutoRetriesNextServer: 2
      ActiveConnectionsLimit: 2147483647
      eureka:
        enabled: true
    hystrix:
      command:
        default:
          execution:
            timeout:
              enabled: true
            isolation:
              thread:
                timeoutInMilliseconds: 600000
    ###超时配置###
    

    6.mss-transaction 消息总线

    这里采用rabbitmq来实现,配置文件如下:

    
    server:
      port: 9070
    spring:
      application:
        name: mss-transaction
      datasource:
        driver-class-name: com.mysql.jdbc.Driver
        url: jdbc:mysql://***:4306/nsinformation?useUnicode=true&characterEncoding=utf-8
        username: nsinformation
        password: ***
      rabbitmq:
        host: localhost
        port: 5672
        username: guest
        password: guest
        publisher-confirms: true
        virtual-host: /
    eureka:
      instance:
        prefer-ip-address: true
        instance-id: ${spring.cloud.client.ip-address}:${server.port}
        ##续约更新时间间隔设置5秒,m默认30s
        lease-renewal-interval-in-seconds: 1
        ##续约到期时间10秒,默认是90秒
        lease-expiration-duration-in-seconds: 2
      client:
          service-url:
            defaultZone: http://mss-eureka:9010/eureka/
    

    7.展示

    7.1分别启动

    register、auth-center、mss-gateway、mss-upms


    7.1启动图页.png
    7.1.1eureka界面图.png

    7.2获取token

    http://localhost:9030/uaa/oauth/token

    用postman 设置basic Auth(可在数据库oauth_client_details表中设置)
    请求参数为:
    grant_type:password
    username:admin
    password:123456
    
    image.png
    image.png
    image.png

    7.3刷新token

    image.png

    7.4获取资源(调用其他接口)

    http://localhost:9030/upms/user/test**](http://localhost:9030/upms/user/test)
    注意:****heard****头部添加:****Authorization 值:bearer +token

    image.png

    总结

    由于刚开始学习spring cloud以及对spring security不太了解,对oauth2.0规范也进行了查缺补漏,基本框架已经搭建完毕。
    源码地址
    源码地址
    源码地址

    相关文章

      网友评论

        本文标题:微服务开发基础框架学习与使用

        本文链接:https://www.haomeiwen.com/subject/ztybdqtx.html