SSOは、同社が前面表示Freemarkを使用し、プロジェクト、SpringMVC、MyBatisの、データベースの使用MySQLを使用してバックエンドの数年間存在しています。今年は、プロジェクトでは、我々はSpringCloudアーキテクチャに変換革命的な改善、およびフロントとリアエンド、フロントエンドのVueの枠組みの分離を持っていました。
まず、アーキテクチャの使用はSpringCloudを変換します
1.1なぜSpringCloud
コアは、従来のばねと比較して、SpringCloud SpringBootあり、SpringCloudは、以下の利点を有します。
- シンプルを展開、SpringBootは、Tomcatコンテナを構築し、それが直接のjava-jarファイルが運営するJARプログラムに翻訳することができます。
- 簡単なコーディング、SpringBootはちょうどそれは、開発者はすぐに非常に便利な、Webコンテナを起動することができ、POMファイル内の依存スターター・ウェブを追加する必要があります。
- 設定は簡単な注釈モードを通じて元春のXML非常に複雑な方法を置き換えるために、単純な、SpringBoot可能です。私は春に、通常のクラスの管理をしたい場合は、単に@Configurationと@Beanを追加する2つのノートをすることができます。
- モニタリングは簡単です、我々は春・ブート開始-アクチュエータを導入することができ、監視の目的を達成するために直接、プロセスの実行時の性能パラメータを取得するためにRESTの方法を使用して依存しています。
1.2は、どのような変革の定期的な一部を投影する必要があります
1.2.1プロフィール
コンフィギュレーションファイルの膨大な数の前にSSOの改修プロジェクトは、主に次のセクションが含まれます。
- 静的リソース関連
- データソース
- MyBatisの設定
- Redisの設定
- 業務
- 内容を傍受するインターセプタ
- モニター、フィルター
- スキャンパス構成成分
この記事では、次のセクションに焦点を当てています。
1)静的リソースの扱い
場合SpringMVCは、mvc:interceptors
次のようにURLのルールの設定があり、それは静的リソースをインターセプトしません。
<mvc:mapping path="/*.do" />
ただし、設定がある場合:
<mvc:mapping path="/**" />
スキーム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>
シナリオ2:使用する<mvc:resources />
ラベルは、静的リソースパスを宣言します
<mvc:resources mapping="/resources/js/**" location="/js/" />
<mvc:resources mapping="/resources/images/**" location="/images/" />
<mvc:resources mapping="/resources/css/**" location="/css/" />
オプション3:使用するmvc:default-servlet-handler/
ラベル
SpringBootソリューション:addResourceHandlers方法を達成WebMvcConfigurerAdapter継承。
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/resource/")//sso静态资源
.addResourceLocations("classpath:/META-INF/resources/")//swagger静态资源
.setCachePeriod(0);//0表示不缓存
}
示すように、SSO静的リソースファイルのパス:
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>
SpringBoot単にWebMvcConfigurerAdapterを拡張し、この方法は、あることができるaddInterceptorsをオーバーライドインターセプタを追加しました。
/*** 拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(permissionInterceptor).
addPathPatterns("/**");
super.addInterceptors(registry);
}
カスタムインターセプタは、いくつかのパラメータを初期化する必要があり、したがって、インターセプタを登録する前に登録する必要があり、ここでは遅延ロードを設定します。ログイン傍受フリーパス、およびファイルをログインした後、裁判官の許可を必要としないパスは、システム環境変数環境値を介して取得するYMLで書かれています。
@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;
}
3)構成データベースとMyBatisの
、データソースの構成
データソースは3例を注射さ:
[I.]
- 条件:なしプライマーはドルイドスプリング・ブート・スターターはspring.datasource.typeを指定していない、だけdruid.jarに依存STARTません。
- 結果:データソースは、Tomcatのデータソースを注入しています。
- 分析:四種類デフォルトがあれば依存MyBatisのスプリングブートスタータプロジェクトはTomcatのデータソースは、DataSourceAutoConfiguration自動注射クラスのばねブートAUTOCONFIGURE-スタータするデータソースを指定せずに、パスを決定依拠が注入されている場合、データソース(ひかり、Tomcatの、DBCP、Dbcp2)は、です。
[ケース2]
- 条件:ドルイドスプリングブート・スターターを導入せずにspring.datasource.type DruidDataSourceとして指定のみdruid.jarに依存します。
- 結果:注入されたDruidDataSourceデータソースが、有効になりませんドルイド設定のプロファイル。
- 分析:春、データソースの依存関係を指定し、自動的に注入しますスターターYMLのドルイドは、データソースを指定し、データソースのインプラントを指定。@ConfigurationPropertiesは、性能パラメータを処理DataSourcePropertiesはドルイド部、ソースの唯一の属性データ処理部を属性注釈を付けていません。
[三]ケース
- 条件:プライマーSTARTドルイドスプリング・ブート・スターターはspring.datasource.typeのDruidDataSourceとして指定され、druid.jarに依存しません。
- 結果:注入されたDruidDataSourceデータソース、ドルイド構成プロファイルが有効になります。
- 分析:ドルイド・春・ブート・スターター自動的DataSourceAutoConfiguration前に、最初に作成したデータソースクラスを設定し、@ConfigurationPropertiesはドルイドプロファイルは属性が含まれDataSourceProperties注入します。
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>
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-return: false # 在连接归还到连接池时是否测试该连接
filters: config,wall,stat
B、MyBatisの設定
MyBatisのスプリング・ブート・スターターは依存導入することで、あなたはMyBatisのシンプルな構成を使用することを開始することができます。
次のような単純な分析MyBatisのスターターソースコードとどのようにMyBatisのを設定します。
spring.factoriesファイルのMyBatisのスプリング・ブート・自動構成でMyBatisのスプリング・ブート・スターターを見てください
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
あなたは知ることができるデータソースが、このクラスからsqlSessionFactory MyBatisのがロードされます後、あなたが最初に作成しなければならないクラスは自動的にMybatisAutoConfigurationを注入され、我々は分析を始める見ることができます。
@EnableConfigurationProperties({MybatisProperties.class})構成ファイルの注釈で指定され
=「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();
}
}
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;
}
MyBatisのを使用してC、
- プロファイル:
application.yml
mybatis:
config-location: classpath:mybatis.xml # mybatis配置文件所在路径
type-aliases-package: com.creditease.permission.model # 所有Entity别名类所在包
mapper-locations: classpath:mybatis/**/*.xml
上記MybatisPropertiesから分かるように、MyBatisのようなカスタムインターセプタ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>
- 起動クラスに@MapperScanノートに参加
@MapperScan("com.creditease.permission.dao")//mapper类所在目录
public class SsoApplication {
public static void main(String[] args) {
SpringApplication.run(SsoApplication.class, args);
}
}
4)トランザクション
春のトランザクションは、2つの方法で取り扱わ:
- プログラマ
TransactionTemplate PlatformTransactionManagerまたは直接コードで書かれた基本的な業務トランザクションコード付き。
利点:トランザクションは、コードブロックでより柔軟に処理することができます。
短所:侵襲的なコード。
- 宣言型
インターセプト法前後@Transactional注釈またはファイルベースのコンフィギュレーションモードを使用して、。
長所:非侵襲的には、コードを汚染しません。
短所:コントロール、小さい粒子サイズのトランザクションができる唯一の方法とクラス。
、@Transactionalコメント
非SpringBootエンジニアリング、コンフィギュレーションは、コンフィギュレーションファイルに追加する必要があります:
<tx:annotation-driven/>
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>
Javaコードに基づいて、変換後SpringBoot:
@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());
}
5)グローバル例外ハンドラ
同じ例外処理コードを複数回書き込まれ、一般的なコーディングは、時々コードが十分に優雅されないように、一度、複数の例外、のtry-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>
異常/許可/ noSecurity発生後SimpleMappingExceptionResolver例外処理、提供て、NoPermissionExceptionのカスタム例外タイプ、および要求されたパスを使用してください。
SpringBootは@RestControllerAdvice @ControllerAdviceまたは設定されたグローバル例外クラスを使用していました。これは、2つの@Controllerと@RestControllerコメントの違いに似ています。
;て、NoPermissionExceptionの異常や例外を校正カスタムパラメータの一般的な例外処理:グローバルSSOは、例外処理の3種類を定義します。
グローバル例外ハンドラのコードは次のとおりです。
@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);
}
}
@RestControllerAdvice組み合わせ@Validationは、ビーンはは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;
sysCodeは、その引数が空になると、プロセスをキャプチャすることは、パラメータのJSON形式を返す、はBindExceptionグローバル例外処理クラスをスローされます。
{
"resultCode":2,
"resultMsg":"系统编号不能为空",
"resultData":null
}
1.3留意事項
内蔵バージョン1.3.1 Tomcatの問題が高すぎるによって引き起こされます
SpringBoot1.5のtomcat8.5バージョン組み込みデフォルト、および元SpringMVC tomcat7に配備SSO。Tomcatのアップグレードは、最も明白な影響を与え、この変換のためのクッキーです。
tomcat8クッキーチェックプロトコルが使用された後Rfc6265CookieProcessorです。契約は、これらの規則に従わなければならないドメイン名を呼び出します。
- 。これらの文字 - 1-9、AZ、AZ、でなければなりません。
- 数値であるか(creditease.corpにTomcatのCookieドメインの検証異常、そして最終的に与えられる前に.creditease.corpに基づく)文字で開始する必要があります。
- これは、数字または文字の終わりでなければなりません。
第二の、別個の前端と後端
2.1クロスドメインの問題を解決
それは2つの異なるアプリケーションであるため、2つの異なるポートがなければなりません。異なるポートクロスドメインの問題を持って、SSOの実施形態は、nginxの区別可能なリバースプロキシ要求を介して、フロントエンドからの要求が異なるサービスに対応して使用します。
- sso.creditease.comは、バックエンド・アプリケーション・サービスに対応しています。
- sso.creditease.com/webは、静的リソースアプリケーションのフロントに対応しています。
2.2 FBIは、効率的な導入を促進闊歩
闊歩コードインターセプタを変更することによって、プラグインを表示するためのバックエンドインターフェイスである、モックオブジェクトは、フロント及びデバッグインタフェースの後端にログインフリー、ダイレクトアクセスログイン。あなたがパスを見ることができるプラグイン闊歩し、特定のインターフェイスのリクエストパラメータで、パラメータがように、戻り値、インターフェイスの統計情報とは、必要です。
-
インタフェース統計
- パスリクエストパラメータと
- 返却値
2.3インタフェースの変更をジャンプ
modeAndviewの前に道でSpringMvcジャンプは現在、2回の治療を行って:
- フォームに安らかなインタフェースは、制御のフロントエンドは、データを取得するために直接ジャンプします。
- 直接response.sendRedirectジャンプページから。
注意:このデフォルトは、URLの復帰後jessionIDが追加されます、「インデックスをリダイレクトする」リターン:古いコード分岐は、次のようなリターンのパスをリダイレクト追加する前にSpringMvcページの形で使用されています。
発生する可能性のあるアドレスの問題2.4静的リソースの変更
関連するチェックコードの特定のノート・パスの。例えば、変換処理の変更のパスで次の側面に影響を与えます。
- 人の前に、権限のメニューをチェックして、パスの役割は、メニューへのアクセス・パスを変更するために、バインドされたときに無権限につながります。
- スキャンコードのログインインターフェース判定ソースを参照し、パス要求が失敗した変更します。
- SSO-ドームプロジェクトは静的リソースを参照する前に、404が報告されるパスを変更します。
著者:黄Lingfeng
出典:テクノロジーのCreditEase研究所