|
|
||
|---|---|---|
| images | ||
| mystyle-cloud-admin-manager | ||
| mystyle-cloud-admin-monitor | ||
| mystyle-cloud-api | ||
| mystyle-cloud-blog | ||
| mystyle-cloud-canal | ||
| mystyle-cloud-fiction | ||
| mystyle-cloud-file | ||
| mystyle-cloud-gateway | ||
| mystyle-cloud-mail | ||
| mystyle-cloud-model | ||
| mystyle-cloud-mq | ||
| mystyle-cloud-oauth | ||
| mystyle-cloud-quartz | ||
| sql | ||
| .gitignore | ||
| README.md | ||
| mystyle-cloud-parent.iml | ||
| pom.xml | ||
README.md
1. 技术选型
-
注册中心:nacos,替代方案eureka、consul、zookeeper
-
配置中心: nacos ,替代方案sc config、consul config
-
服务调用:feign,替代方案:resttempate
-
熔断:sentinel、,替代方案:Resilience4j
-
熔断监控:sentinel dashboard
-
负载均衡:sc loadbalancer
-
网关:spring cloud gateway
-
链路:spring cloud sleuth+zipkin,替代方案:skywalking等。
-
分时任务调度框架 xxl-job
-
admin 监控
2.工程搭建
2.1 搭建父工程
- mystyle-cloud-parent
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhangmeng</groupId>
<artifactId>mystyle-cloud-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
<spring-boot.version>2.4.4</spring-boot.version>
<spring-cloud.version>2020.0.2</spring-cloud.version>
<spring-cloud-alibaba.version>2020.0.RC1</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- spring boot 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- spring cloud alibaba 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2.2 搭建用户中心(废除)
- mystyle-cloud-user
包结构 : com.zhangmeng.user
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
2.3 搭建文件中心
- mystyle-cloud-file
包结构 :com.zhangmeng.file
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
</dependencies>
2.4 搭建网关
- mystyle-cloud-gateway
包结构 :com.zhangmeng.gateway
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
</dependencies>
- 创建一个网关分组和网关的限流规则
@Configuration
public class GatewayConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
// Register the block exception handler for Spring Cloud Gateway.
return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
@PostConstruct
public void doInit() {
initCustomizedApis();
initGatewayRules();
}
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("consumer")
.setPredicateItems(new HashSet<ApiPredicateItem>() );
ApiDefinition api2 = new ApiDefinition("provider")
.setPredicateItems(new HashSet<ApiPredicateItem>() );
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
private void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("consumer")
.setCount(10)
.setIntervalSec(1)
);
rules.add(new GatewayFlowRule("consumer")
.setCount(2)
.setIntervalSec(2)
.setBurst(2)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
)
);
rules.add(new GatewayFlowRule("provider")
.setCount(10)
.setIntervalSec(1)
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
.setMaxQueueingTimeoutMs(600)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
.setFieldName("X-Sentinel-Flag")
)
);
rules.add(new GatewayFlowRule("provider")
.setCount(1)
.setIntervalSec(1)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("pa")
)
);
rules.add(new GatewayFlowRule("provider")
.setCount(2)
.setIntervalSec(30)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("type")
.setPattern("warn")
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS)
)
);
rules.add(new GatewayFlowRule("provider")
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(5)
.setIntervalSec(1)
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
.setFieldName("pn")
)
);
GatewayRuleManager.loadRules(rules);
}
}
2.5 sentinel dashboard
java -Dserver.port=8748 -Dcsp.sentinel.dashboard.server=localhost:8748 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
添加流控规则 :
2.6 搭建注册中心
2.7 搭建 zipkin server
批处理 启动
@echo off
start /d "E:\nacos-server-2.0.2\nacos\bin\" startup.cmd
start cmd /c "title sentinel-dashboard && java -jar E:\nacos-server-2.0.2\sentinel-dashboard-1.8.2.jar --server.port=8748 &"
start cmd /c "title zipkin-server-2.23.4-exec && java -jar E:\nacos-server-2.0.2\zipkin-server-2.23.4-exec.jar --server.port=9411 &"
pause
2.8 搭建 model
- mystyle-cloud-model
包结构 :com.zhangmeng.model
集成 jpa 及 通用 mapper
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@ComponentScan(basePackages = {"com.zhangmeng.model","com.zhangmeng.file"})
public class FileApplication {
public static void main(String[] args) {
SpringApplication.run(FileApplication.class,args);
}
}
2.8 搭建 管理后台
- mystyle-cloud-admin-manager
包结构 :com.zhangmeng.admin.manager
2.9 api 搭建 api
- mystyle-cloud-api
包结构 :com.zhangmeng.api
package com.zhangmeng.api.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
/**
* @author zhangmeng
* @version 1.0
* @date 2021年7月8日09:12:01
*/
@EnableSwagger2
@Configuration
public class SwaggerConfig {
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2).groupName("swagger接口文档")
.apiInfo(new ApiInfoBuilder().title("swagger接口文档")
.contact(new Contact("转身的背影在心底里沉沦", "", "1334717033.com")).version("1.0").build())
.securitySchemes(securitySchemes())
.securityContexts(securityContexts())
.select()
.apis(RequestHandlerSelectors.basePackage("com.zhangmeng"))
.paths(PathSelectors.any()).build();
}
private List<ApiKey> securitySchemes() {
List<ApiKey> apiKeyList= new ArrayList<>();
apiKeyList.add(new ApiKey("token", "token", "header"));
return apiKeyList;
}
private List<SecurityContext> securityContexts() {
List<SecurityContext> securityContexts=new ArrayList<>();
securityContexts.add(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!auth).*$"))
.build());
return securityContexts;
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
List<SecurityReference> securityReferences=new ArrayList<>();
securityReferences.add(new SecurityReference("token", authorizationScopes));
return securityReferences;
}
}
2.10 搭建 canal
- mystyle-cloud-canal
包结构 :com.zhangmeng.canal
(2) 创建账号 用于测试使用,
使用root账号创建用户并授予权限
create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCanalClient
public class CanalApplication {
public static void main(String[] args) {
SpringApplication.run(CanalApplication.class,args);
}
}
2.11 搭建 消息中间件 rabbit mq
- mystyle-cloud-mq
包结构 :com.zhangmeng.mq
2.12 搭建 授权中心
- mystyle-cloud-oauth
包结构 :com.zhangmeng.oauth
- Encoded password does not look like BCrypt
2.13 搭建博客
-
mystyle-cloud-blog
包结构 :com.zhangmeng.blog
2.14 搭建定时任务
思路 :
quartz + rabbit_mq
xxl-job 分布式调度框架
后续调整为:
- mystyle-cloud-xxl-job
com.zhangmeng.job
引入依赖
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>
2.15 搭建邮件
- mystyle-cloud-mail
包结构 :com.zhangmeng.mail
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2.15 搭建小说
- mystyle-cloud-fiction
包结构 :com.zhangmeng.fiction
2.16 搭建admin 监控
- mystyle-cloud-admin-monitor
包结构 :com.zhangmeng.monitor
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
enabled: true
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
- /actuator/**
- /instances/**
3.接口说明
3.1 登录接口
3.1.1 验证码
3.1.2 查询当前用户
3.1.3 登录
- 解决办法
gate_way yml 配置
server:
port: 9000
spring:
application:
name: mystyle-cloud-gateway
zipkin:
sender:
type: web
base-url: http://localhost:9411/
service:
name: mystyle-cloud-gateway
sleuth:
sampler:
probability: 1
cloud:
sentinel:
transport:
port: 15000
dashboard: localhost:8748
nacos:
discovery:
server-addr: 127.0.0.1:8848
gateway:
globalcors:
corsConfigurations:
'[/**]': # 匹配所有请求
allowedOrigins: "*" #跨域处理 允许所有的域
allowedMethods: # 支持的方法
- GET
- POST
- PUT
- DELETE
allowedOriginPatterns: "*"
discovery:
locator:
enabled: false
lowerCaseServiceId: true
routes:
- id: mystyle-cloud-oauth
uri: lb://mystyle-cloud-oauth
predicates:
- Path=/mystyle-cloud-oauth/**
filters:
- StripPrefix=1
- id: mystyle-cloud-admin-manager
uri: lb://mystyle-cloud-admin-manager
predicates:
- Path=/mystyle-cloud-admin-manager/**
filters:
- StripPrefix=1
或者
package com.zhangmeng.gateway.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
/**
* @author zhangmeng
* @date 2021年11月8日15:52:27
* @version 1.0
*/
@Configuration
public class CorsConfig {
@Bean
public CorsWebFilter corsWebFilter(){
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//1、配置跨域
//允许哪些头进行跨域
corsConfiguration.addAllowedHeader("*");
//允许哪些请求方式进行跨域
corsConfiguration.addAllowedMethod("*");
//允许哪些请求来源进行跨域
corsConfiguration.addAllowedOriginPattern("*");
//是否允许携带cookie进行跨域,否则跨域请求会丢失cookie信息
corsConfiguration.setAllowCredentials(true);
source.registerCorsConfiguration("/**",corsConfiguration);
return new CorsWebFilter(source);
}
}
提示 : 调用的具体服务去掉跨域配置
3.2 首页
获取鉴权信息
- 源码
oauth2.0 获取鉴权
/**
* Copyright 2006-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.springframework.security.oauth2.provider.authentication;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.util.Assert;
/**
* A pre-authentication filter for OAuth2 protected resources. Extracts an OAuth2 token from the incoming request and
* uses it to populate the Spring Security context with an {@link OAuth2Authentication} (if used in conjunction with an
* {@link OAuth2AuthenticationManager}).
*
* @author Dave Syer
*
*/
public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
private final static Log logger = LogFactory.getLog(OAuth2AuthenticationProcessingFilter.class);
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
private AuthenticationManager authenticationManager;
private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new OAuth2AuthenticationDetailsSource();
private TokenExtractor tokenExtractor = new BearerTokenExtractor();
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
private boolean stateless = true;
/**
* Flag to say that this filter guards stateless resources (default true). Set this to true if the only way the
* resource can be accessed is with a token. If false then an incoming cookie can populate the security context and
* allow access to a caller that isn't an OAuth2 client.
*
* @param stateless the flag to set (default true)
*/
public void setStateless(boolean stateless) {
this.stateless = stateless;
}
/**
* @param authenticationEntryPoint the authentication entry point to set
*/
public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {
this.authenticationEntryPoint = authenticationEntryPoint;
}
/**
* @param authenticationManager the authentication manager to set (mandatory with no default)
*/
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
}
/**
* @param tokenExtractor the tokenExtractor to set
*/
public void setTokenExtractor(TokenExtractor tokenExtractor) {
this.tokenExtractor = tokenExtractor;
}
/**
* @param eventPublisher the event publisher to set
*/
public void setAuthenticationEventPublisher(AuthenticationEventPublisher eventPublisher) {
this.eventPublisher = eventPublisher;
}
/**
* @param authenticationDetailsSource The AuthenticationDetailsSource to use
*/
public void setAuthenticationDetailsSource(
AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource) {
Assert.notNull(authenticationDetailsSource, "AuthenticationDetailsSource required");
this.authenticationDetailsSource = authenticationDetailsSource;
}
public void afterPropertiesSet() {
Assert.state(authenticationManager != null, "AuthenticationManager is required");
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
ServletException {
final boolean debug = logger.isDebugEnabled();
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
try {
Authentication authentication = tokenExtractor.extract(request);
if (authentication == null) {
if (stateless && isAuthenticated()) {
if (debug) {
logger.debug("Clearing security context.");
}
SecurityContextHolder.clearContext();
}
if (debug) {
logger.debug("No token in request, will continue chain.");
}
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
if (authentication instanceof AbstractAuthenticationToken) {
AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
}
Authentication authResult = authenticationManager.authenticate(authentication);
if (debug) {
logger.debug("Authentication success: " + authResult);
}
eventPublisher.publishAuthenticationSuccess(authResult);
SecurityContextHolder.getContext().setAuthentication(authResult);
}
}
catch (OAuth2Exception failed) {
SecurityContextHolder.clearContext();
if (debug) {
logger.debug("Authentication request failed: " + failed);
}
eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed),
new PreAuthenticatedAuthenticationToken("access-token", "N/A"));
authenticationEntryPoint.commence(request, response,
new InsufficientAuthenticationException(failed.getMessage(), failed));
return;
}
chain.doFilter(request, response);
}
private boolean isAuthenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
return false;
}
return true;
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void destroy() {
}
private static final class NullEventPublisher implements AuthenticationEventPublisher {
public void publishAuthenticationFailure(AuthenticationException exception, Authentication authentication) {
}
public void publishAuthenticationSuccess(Authentication authentication) {
}
}
}
3.2.1 先从请求头中获取
protected String extractToken(HttpServletRequest request) {
// first check the header...
--------------------------------------------------------------
String token = extractHeaderToken(request);
--------------------------------------------------------------
// bearer type allows a request parameter as well
if (token == null) {
logger.debug("Token not found in headers. Trying request parameters.");
token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
if (token == null) {
logger.debug("Token not found in request parameters. Not an OAuth2 request.");
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
}
}
return token;
}
/**
* Extract the OAuth bearer token from a header.
*
* @param request The request.
* @return The token, or null if no OAuth authorization header was supplied.
*/
protected String extractHeaderToken(HttpServletRequest request) {
---------------------------------------------------------------------------
Enumeration<String> headers = request.getHeaders("Authorization");
----------------------------------------------------------------------------
while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
String value = headers.nextElement();
if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
// Add this here for the auth details later. Would be better to change the signature of this method.
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
int commaIndex = authHeaderValue.indexOf(',');
if (commaIndex > 0) {
authHeaderValue = authHeaderValue.substring(0, commaIndex);
}
return authHeaderValue;
}
}
return null;
}
3.2.2 从请求参数中获取
protected String extractToken(HttpServletRequest request) {
// first check the header...
String token = extractHeaderToken(request);
// bearer type allows a request parameter as well
if (token == null) {
logger.debug("Token not found in headers. Trying request parameters.");
-------------------------------------------------------------------
token = request.getParameter(OAuth2AccessToken.ACCESS_TOKEN);
-------------------------------------------------------------------
if (token == null) {
logger.debug("Token not found in request parameters. Not an OAuth2 request.");
}
else {
request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE, OAuth2AccessToken.BEARER_TYPE);
}
}
return token;
}
3.2.3 问题
首页不展示
控制台报错 Refused to display 'http://localhost:9000/' in a frame because it set 'X-Frame-Options' to 'deny'.
解决办法
http.headers().frameOptions().disable();
token 过期展示
3.3 文件上传
解决办法
spring:
servlet:
multipart:
max-file-size: 4GB
max-request-size: 4GB
3.4 feign 远程调用
报错 :nested exception is feign.RetryableException: too many bytes written executing
- 解决办法
// 跳过 content-length
if (element.equals("content-length")){
continue;
}
@Configuration
public class FeignOauth2RequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
// 获取的全部请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null){
HttpServletRequest request = attributes.getRequest();
// 获取所有的请求头信息
Enumeration<String> headerNames = request.getHeaderNames();
if (headerNames != null){
while (headerNames.hasMoreElements()){
// 获取请求头的key
String element = headerNames.nextElement();
// 获取请求头的value
String value = request.getHeader(element);
----------------------------------------------------------------------------------------------------------------
// 跳过 content-length
if (element.equals("content-length")){
continue;
}
----------------------------------------------------------------------------------------------------------------
// 将请求头信息放入到请求头
requestTemplate.header(element,value);
}
}
}
}
}




































