一个项目的SpringCloud微服务改造过程

开发 开发工具 架构
SSO是公司一个已经存在了若干年的项目,后端采用SpringMVC、MyBatis,数据库使用MySQL,前端展示使用Freemark。今年,我们对该项目进行了一次革命性的改进。

SSO是公司一个已经存在了若干年的项目,后端采用SpringMVC、MyBatis,数据库使用MySQL,前端展示使用Freemark。今年,我们对该项目进行了一次革命性的改进,将其改造成SpringCloud架构,并且把前后端分离,前端采用Vue框架。 

一、使用SpringCloud架构进行改造 

1.1 为什么使用SpringCloud  

 

SpringCloud的核心是SpringBoot,相比较于传统的Spring,SpringCloud具有以下优点:

  • 部署简单,SpringBoot内置了Tomcat容器,可以将程序直接编译成一个jar,通过java-jar来运行。
  • 编码简单,SpringBoot只需要在pom文件中添加一个starter-web依赖,即可帮助开发者快速启动一个web容器,非常方便。
  • 配置简单,SpringBoot可以通过简单的注解方式来代替原先Spring非常复杂的xml方式。如果我想把一个普通的类交给Spring管理,只需要添加@Configuration和@Bean两个注解即可。
  • 监控简单,我们可以引入spring-boot-start-actuator依赖,直接使用REST方式来获取进程的运行期性能参数,从而达到监控的目的。

1.2 一个常规项目都需要改造哪些部分

1.2.1 配置文件  

 

SSO项目改造前充斥着大量的配置文件,主要包含以下这些部分:

  • 静态资源相关
  • 数据源
  • mybatis配置
  • redis配置
  • 事务
  • 拦截器拦截内容
  • 监听器、过滤器
  • 组件扫描路径配置

 

本文着重介绍以下几个部分:

1)静态资源处理 

 

SpringMVC中,如果mvc:interceptors配置的URL规则如下,则不会拦截静态资源。

<mvc:mapping path="/*.do" /> 
  • 1.

但是如果配置的是:

<mvc:mapping path="/**" /> 
  • 1.

 

方案1: 在web.xml中配置<servlet-name>default</servlet-name>,用defaultServlet先处理请求如:

<servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.jpg</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.png</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.gif</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.ico</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.gif</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.js</url-pattern> 
    </servlet-mapping> 
    <servlet-mapping> 
        <servlet-name>default</servlet-name> 
        <url-pattern>*.css</url-pattern> 
    </servlet-mapping> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

 方案2使用<mvc:resources />标签声明静态资源路径

<mvc:resources mapping="/resources/js/**" location="/js/" /> 
<mvc:resources mapping="/resources/images/**" location="/images/" /> 
<mvc:resources mapping="/resources/css/**" location="/css/" /> 
  • 1.
  • 2.
  • 3.

方案3:使用mvc:default-servlet-handler/标签

SpringBoot解决方案:继承WebMvcConfigurerAdapter实现addResourceHandlers方法。

public void addResourceHandlers(ResourceHandlerRegistry registry) { 
    registry.addResourceHandler("/**"
    .addResourceLocations("classpath:/resource/")//sso静态资源 
    .addResourceLocations("classpath:/META-INF/resources/")//swagger静态资源 
    .setCachePeriod(0);//0表示不缓存 
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.

 sso静态资源文件路径如图: 

一个项目的SpringCloud微服务改造过程

2)拦截器

SpringMVC配置文件内容:

拦截任何请求并且初始化参数,有些请求是不需要拦截的,有的请求登录后不需要经过权限校验直接放行。

<mvc:interceptors> 
    <mvc:interceptor> 
        <mvc:mapping path="/**" /> 
           <bean class="自定义拦截器PermissionInterceptor"
           <!-- 未登录即可访问的地址 --> 
          <property name="excludeUrls"
          <list><value>请求地址<value></list> 
          </property> 
          <!-- 只要登录了就不需要拦截的资源 --> 
          <property name="LogInExcludeUrls"
          <list><value>请求地址<value></list> 
          </property> 
         </bean> 
   </mvc:interceptor> 
 </mvc:interceptors> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 SpringBoot中添加拦截器只需继承WebMvcConfigurerAdapter,并重写addInterceptors方法即可。

/*** 拦截器 
* @param registry 
*/ 
@Override 
public void addInterceptors(InterceptorRegistry registry) { 
   registry.addInterceptor(permissionInterceptor). 
           addPathPatterns("/**"); 
   super.addInterceptors(registry); 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.

自定义的拦截器需要初始化一些参数,因此需要在注册拦截器之前注册,这里我们设置为懒加载。免登录拦截的路径,以及登录后不需要判断权限的路径都写在yml文件了,通过系统环境变量Environment获取值。

@Autowired  
@Lazy  
private PermissionInterceptor permissionInterceptor;  
@Autowired  
private Environment environment;  
/** 
* 
*/ 
@Bean 
public PermissionInterceptor permissionInterceptor() { 
PermissionInterceptor permissionInterceptor = new PermissionInterceptor(); 
List<String> excludeUrls = Arrays.asList(environment.getProperty("intercept.exclude.path").split(",")); 
List<String> commonUrls = Arrays.asList(environment.getProperty("intercept.login.exclude.path").split(",")); 
permissionInterceptor.setCommonUrls(commonUrls); 
permissionInterceptor.setExcludeUrls(excludeUrls); 
return permissionInterceptor; 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.

 3)数据库和MyBatis配置  

 

A、数据源配置
数据源注入的三种情况:
【情况一】

  • 条件:不引⼊druid-spring-boot-starter只依赖druid.jar,不指定spring.datasource.type。 

  • 结果:注入的数据源是tomcat的数据源。 

  • 解析:依赖的mybatis-spring-boot-starter工程依赖了tomcat的数据源,spring-boot-autoconfigure-starter的DataSourceAutoConfiguration自动注入类会在不指定数据源的情况下,判断路径中是否存在默认的4种数据源(Hikari,Tomcat,Dbcp,Dbcp2)的其一,如果有就注入。  

 

【情况二】

  • 条件:不引入druid-spring-boot-starter只依赖druid.jar ,指定spring.datasource.type为DruidDataSource。
  • 结果:注入了DruidDataSource数据源,但配置文件中的druid配置不会生效。 
  • 解析: 指定了依赖的数据源后,spring自动注入的starter会将指定的数据源注入,yml指定了druid数据源。@ConfigurationProperties注解的DataSourceProperties没处理druid部分的性能参数属性,只处理了数据源部分的属性。

 【情况三】 

  • 条件:引⼊ druid-spring-boot-starter 不依赖druid.jar,指定spring.datasource.type为DruidDataSource。
  • 结果:注入了DruidDataSource数据源, 配置文件中的druid配置也会生效。 
  • 解析:druid-spring-boot-starter自动配置类会在DataSourceAutoConfiguration之前先创建数据源,并且@ConfigurationProperties注入的DataSourceProperties包含了配置文件中druid的属性。

 pom.xml依赖: 

<!-- 情况一、二 测试引入的依赖 --> 
  <!--<dependency>--> 
      <!--<groupId>com.alibaba</groupId>--> 
      <!--<artifactId>druid</artifactId>--> 
      <!--<version>${druid.version}</version>--> 
  <!--</dependency>--> 
  <dependency> 
      <groupId>com.alibaba</groupId> 
      <artifactId>druid-spring-boot-starter</artifactId> 
      <version>1.1.10</version> 
  </dependency> 
  <dependency> 
      <groupId>org.mybatis.spring.boot</groupId> 
      <artifactId>mybatis-spring-boot-starter</artifactId> 
      <version>RELEASE</version> 
  </dependency> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.

yml配置:

spring: 
  datasource: 
    type: com.alibaba.druid.pool.DruidDataSource            # 当前数据源操作类型 
    driver-class-name: com.mysql.jdbc.Driver            # mysql驱动包 
    url: jdbc:mysql://yourURL        # 数据库名称 
    username: yourusername 
    password: yourpassword 
    druid: 
      initial-size: 5  # 初始化大小 
      min-idle: 5  # 最小 
      max-active: 20  # 最大 
      max-wait: 60000  # 连接超时时间 
      time-between-eviction-runs-millis: 60000  # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 
      min-evictable-idle-time-millis: 300000  # 指定一个空闲连接最少空闲多久后可被清除,单位是毫秒 
      validationQuery: select 'x' 
      test-while-idle: true  # 当连接空闲时,是否执行连接测试 
      test-on-borrow: false  # 当从连接池借用连接时,是否测试该连接 
      test-on-returnfalse  # 在连接归还到连接池时是否测试该连接 
      filters: config,wall,stat 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

 B、MyBatis配置  

 

通过引入mybatis-spring-boot-starter依赖,可以简单配置mybatis上手使用。
下面简单分析mybatis-starter的源码以及如何配置mybatis。 
先看mybatis-spring-boot-starter中mybatis-spring-boot-autoconfigure的spring.factories文件。

# Auto Configure 
org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration 
  • 1.
  • 2.

 可以看到自动注入类是MybatisAutoConfiguration,我们从这个类入手分析可以知道,必须先创建好了数据源后,才会加载MyBatis的sqlSessionFactory。

@EnableConfigurationProperties({MybatisProperties.class})注解指定了配置文件中 prefix = "mybatis" 那部分属性有效,这部分属性值将注入到已创建的SqlSessionFactoryBean中,最后生成SqlSessionFactory对象。

@Configuration 
//当SqlSessionFactory,SqlSessionFactoryBean存在的情况下加载当前Bean 
@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class}) 
//当指定数据源在容器中只有一个或者有多个但是只指定首选数据源 
@ConditionalOnSingleCandidate(DataSource.class
@EnableConfigurationProperties({MybatisProperties.class}) 
//当数据源注入到Spring容器后才开始加载当前Bean 
@AutoConfigureAfter({DataSourceAutoConfiguration.class}) 
public class MybatisAutoConfiguration implements InitializingBean { 
    private final MybatisProperties properties; 
    @Bean 
    @ConditionalOnMissingBean 
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { 
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean(); 
        factory.setDataSource(dataSource); 
        factory.setVfs(SpringBootVFS.class); 
       //设置mybatis配置文件所在路径 
        if (StringUtils.hasText(this.properties.getConfigLocation())) { 
          factory.setConfigLocation(this.resourceLoader.getResource 
          (this.properties.getConfigLocation())); } 
        } 
      //设置其他MyBatisProperties对象中有的属性略.... 
       return factory.getObject(); 
   } 
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

 MybatisProperties含有的属性: 

@ConfigurationProperties(prefix = "mybatis" ) 
public class MybatisProperties { 
 public static final String MYBATIS_PREFIX = "mybatis"
 private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); 
 private String configLocation; 
 private String[] mapperLocations; 
 private String typeAliasesPackage; 
 private Class<?> typeAliasesSuperType; 
 private String typeHandlersPackage; 
 private boolean checkConfigLocation = false
 private ExecutorType executorType; 
 private Properties configurationProperties; 
 @NestedConfigurationProperty 
 private Configuration configuration; 
} 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

 C、使用MyBatis 

  • 配置文件

application.yml

mybatis: 
config-location: classpath:mybatis.xml        # mybatis配置文件所在路径 
type-aliases-package: com.creditease.permission.model    # 所有Entity别名类所在包 
mapper-locations: classpath:mybatis/**/*.xml 
  • 1.
  • 2.
  • 3.
  • 4.

 从上面的MybatisProperties可以看出,mybatis可以指定一些configuration,比如自定义拦截器pageHelper。

mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?> 
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-config.dtd"
<configuration> 
    <plugins> 
        <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin> 
        <plugin interceptor="com.creditease.permission.manager.MybatisInterceptor"></plugin> 
    </plugins> 
</configuration> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 在启动类上加入@MapperScan注解
@MapperScan("com.creditease.permission.dao")//mapper类所在目录 
public class SsoApplication { 
    public static void main(String[] args) { 
        SpringApplication.run(SsoApplication.class, args); 
    } 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
4)事务 

 

Spring事务有两种处理方式:

  • 编程式

用TransactionTemplate或者直接使用底层的PlatformTransactionManager将事务代码写在业务代码中。
优点:可以在代码块中处理事务,比较灵活。 
缺点:对代码具有侵入性。

  • 声明式

采用@Transactional注解或者基于配置文件方式,在方法前后进行拦截。
优点:非侵入性不会污染代码。
缺点:事务只能在方法和类上控制,粒度较小。
A、使用@Transactional注解
非SpringBoot工程,需要在配置文件中加入配置:

<tx:annotation-driven/> 
 
  • 1.
  • 2.

 

SpringBoot工程可以用@EnableTransactionManagement注解代替上面的配置内容。
B、采用配置文件方式 
之前的sso是基于配置的方式,配置代码如下:

<aop:config> 
      <aop:pointcut expression="execution(public * com.creditease.permission.service.impl.*Impl.*(..))" id="pointcut"/> 
      <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> 
  </aop:config> 
  <tx:advice id="txAdvice" transaction-manager="transactionManager"
      <tx:attributes> 
          <tx:method name="query*" propagation="REQUIRED" read-only="true"/> 
          <tx:method name="find*" propagation="REQUIRED" read-only="true"/> 
          <tx:method name="save*" propagation="REQUIRED"/> 
          <tx:method name="delete*" propagation="REQUIRED"/> 
          <tx:method name="add*" propagation="REQUIRED"/> 
          <tx:method name="modify*" propagation="REQUIRED"/> 
      </tx:attributes> 
  </tx:advice> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

 改造后的SpringBoot基于Java代码: 

 @Aspect 
@Configuration 
public class TransactionAdviceConfig { 
/** 
* 指定切入点 
*/ 
private static final String AOP_POINTCUT_EXPRESSION = "execution(public * com.creditease.permission.service.impl.*Impl.*(..))"
 
@Resource 
DruidDataSource dataSource; 
 
/** 
* 指定处理事务的PlatformTransactionManager 
* @return 
*/ 
@Bean 
public DataSourceTransactionManager transactionManager() { 
 
return new DataSourceTransactionManager(dataSource); 
 

 
/** 
* 指定切入点处理逻辑,执行事务 
* @return 
*/ 
@Bean 
public TransactionInterceptor txAdvice() { 
 
DefaultTransactionAttribute txAttrRequired = new DefaultTransactionAttribute(); 
txAttrRequired.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 
 
DefaultTransactionAttribute txAttrRequiredReadonly = new DefaultTransactionAttribute(); 
txAttrRequiredReadonly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); 
txAttrRequiredReadonly.setReadOnly(true); 
 
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); 
source.addTransactionalMethod("query*", txAttrRequiredReadonly); 
source.addTransactionalMethod("find*", txAttrRequiredReadonly); 
source.addTransactionalMethod("save*", txAttrRequired); 
source.addTransactionalMethod("delete*", txAttrRequired); 
source.addTransactionalMethod("add*", txAttrRequired); 
source.addTransactionalMethod("modify*", txAttrRequired); 
return new TransactionInterceptor(transactionManager(), source); 

 
/** 
* Advisor组装配置,将Advice的代码逻辑注入到Pointcut位置 
* @return 
*/ 
@Bean 
public Advisor txAdviceAdvisor() { 
 
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); 
pointcut.setExpression(AOP_POINTCUT_EXPRESSION); 
return new DefaultPointcutAdvisor(pointcut, txAdvice()); 
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
5)全局异常处理

 

一般编码时有异常我们都会try-catch捕获异常,有时为了区分不同的异常还会一次catch多个异常,大量的try-catch语句,这样使得代码也不够优雅;一个相同的异常处理写多次代码也比较冗余,所以引入全局的异常处理非常必要。
改造前的异常处理配置文件:

<!--定义异常处理页面--> 
<bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"
        <property name="exceptionMappings"
            <props> 
                <prop key="com.creditease.permissionapi.exception.NopermissionException">/permission/noSecurity</prop> 
            </props> 
        </property> 
 </bean> 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

使用SimpleMappingExceptionResolver类处理异常,设置自定义异常类型NopermissionException,以及异常发生后的请求路径/permission/noSecurity。

SpringBoot中采用@RestControllerAdvice或者@ControllerAdvice设置全局异常类。这两者区别类似于@Controller和@RestController注解。
SSO中定义了三种全局的异常处理:普通的Exception处理;自定的NopermissionException异常和参数校验异常。

全局异常处理代码如下:

@Configuration 
@Slf4j 
@RestControllerAdvice 
public class GlobalExceptionConfig { 
//无权限处理 
@ExceptionHandler(value = {NopermissionException.class}) 
public void noPermissionExceptionHandler(HttpServletRequest request, Exception ex, HttpServletResponse response, @Value("${sso.server.prefix}") String domain) throws IOException { 
printLog(request,ex); 
response.sendRedirect("跳转到无权限页面地址"); 

 
//参数校验处理 
@ExceptionHandler(value = {BindException.class}) 
public ResultBody BindExceptionHandler(BindException bindException){ 
List<ObjectError> errors = bindException.getBindingResult().getAllErrors(); 
//这个ResultBody是一个返回结果对象,这里需要返回json,里面包含了状态码和提示信息 
return ResultBody.buildFailureResult(errors.get(0).getDefaultMessage()); 

 
//所有未捕获的异常处理逻辑 
@ExceptionHandler(value = {Exception.class}) 
public ResultBody exceptionHandler(HttpServletRequest request,Exception ex){ 
printLog(request,ex); 
return ResultBody.buildExceptionResult(); 

 
//将请求参数和异常打印出来,结合@slf4j注解 
public void printLog(HttpServletRequest request,Exception ex){ 
String parameters = JsonHelper.toString(request.getParameterMap()); 
log.error("url>>>:{},params>>>:{} ,printLog>>>:{}",request.getRequestURL(),parameters,ex); 

 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.

@RestControllerAdvice结合@Validation,可以对Bean进行校验,校验不通过会抛出BindException异常。通过注解可以少写if-else代码,判断请求的接口参数是否为空,提高代码的美观性。例如:

 //常规做法 
    if(StringUtils.isEmpty(ssoSystem.getSysCode()) 
//SSO做法 
//在Controller请求方法上添加@Valid注解 
@RequestMapping(value = "/add", method = RequestMethod.POST) 
public ResultBody add(@Valid @RequestBody SsoSystem ssoSystem) { 
 

 
//在需要处理的SsoSystem Bean的属性上加@NotNull注解 
@NotNull(message = "系统编号不能为空"
private String sysCode; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.

 当sysCode传入参数为空时,就会抛出BindException被全局的异常处理类,捕获处理返回json格式的参数: 


    "resultCode":2
    "resultMsg":"系统编号不能为空"
    "resultData":null 

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

1.3 注意事项

1.3.1 内置tomcat版本太高引发的问题 

 

SpringBoot1.5默认使用内嵌tomcat8.5版本,而原来SpringMVC的SSO部署在tomcat7上。tomcat的升级对这次改造影响最明显的就是cookie。tomcat8后采用的cookie校验协议是Rfc6265CookieProcessor。该协议要求domain的命名必须遵循以下规则:

  • 必须是1-9、a-z、A-Z、. 、- 这几个字符组成。
  • 必须是数字或字母开头 (之前是以.creditease.corp 会报错tomcat cookie domain validation异常,最后改成了 creditease.corp)。
  • 必须是数字或字母结尾。

 

二、前后端分离
2.1 解决跨域问题

由于是两个不同的应用,必然会有两个不同的端口。不同的端口就会有跨域问题,SSO采用的方式是通过nginx区分来自前后端的请求,反向代理请求对应到不同的服务去。

  • sso.creditease.com对应的是后端的应用服务。
  • sso.creditease.com/web对应的是前端的静态资源应用服务。

2.2 方便联调效率,引入swagger 

 

swagger是后端接口展示的插件,通过修改拦截器代码,mock登录对象免登录,直接访问接口进行前后端的调试。在swagger插件上可以看到具体接口请求路径和参数、参数是否必须、返回值、接口统计信息等。

  • 接口统计信息

一个项目的SpringCloud微服务改造过程 

  • 请求参数和路径

一个项目的SpringCloud微服务改造过程

  • 返回值

一个项目的SpringCloud微服务改造过程

2.3 跳转接口修改

 

 

 

之前是通过SpringMvc的modeAndview方式跳转的,现在做了两种处理: 

 

 

  • 改成restful接口的形式,前端控制跳转然后直接获取数据。 
  • 直接通过response.sendRedirect跳转页面。

 注意:老代码跳转采用的是通过SpringMvc在return的页面路径前加redirect的形式,如:return "redirect:index",这样默认会在return的URL后加jessionID。 

2.4 静态资源地址变更可能引发的问题 

 

特别需要注意代码中的相关校验路径的地方。比如在这次改造过程中路径修改会影响以下几个方面。

  • 菜单权限校验的时候,之前人、角色和路径已经绑定了,修改菜单访问路径会导致没权限。
  • 扫码登录的接口判断了refer来源,修改路径会导致请求失败。
  • 之前的sso-dome工程引用了静态资源,修改路径会报404。

【本文是51CTO专栏机构宜信技术学院的原创文章,微信公众号“宜信技术学院( id: CE_TECH)”】

戳这里,看该作者更多好文

 

责任编辑:张燕妮 来源: 宜信技术学院
相关推荐

2019-01-11 09:41:56

网易考拉服务架构微服务

2017-06-20 14:29:12

Rec开源项目

2012-11-29 09:49:17

软件项目项目

2011-08-25 09:03:40

2017-05-25 12:40:06

SOA微服务系统

2014-08-11 16:32:04

架构项目

2014-08-27 10:20:10

项目项目分析

2017-11-07 11:36:57

开源项目代码

2018-05-09 08:18:26

微服务改造架构

2021-02-24 13:58:07

区块链比特币安全

2022-02-28 08:23:02

开源项目重构

2020-08-13 17:59:20

区块链区块链项目数字货币

2024-04-01 13:18:15

App架构服务端

2020-11-15 23:23:21

JavaScriptAPI开发

2019-01-15 10:02:06

Kubernetes开源工具微服务

2021-05-20 13:22:31

架构运维技术

2012-06-27 10:16:12

开源项目CodePlex

2021-01-22 17:46:37

微服务开源Web

2022-11-08 08:35:53

架构微服务移动

2018-05-30 10:04:38

容器技术微服务
点赞
收藏

51CTO技术栈公众号