mystyle-cloud-parent/README.md

1178 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

### 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.工程搭建
<br>
#### 2.1 搭建父工程
<br>
* mystyle-cloud-parent
```xml
<?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
[mystyle-cloud-user](http://localhost:8848/nacos/)
```xml
<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
[file调用user](http://localhost:8763/file/name?name=转身的背影在心底里沉沦)
![file调用user](images/snipaste_20211030_152133.png)
![文件列表](images/snipaste_20211117_153517.png)
-------
```xml
<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
[测试](http://localhost:5000/mystyle-file/file/name?name=转身的背影在心底里沉沦)
![](images/snipaste_20211030_155720.png)
-------
```xml
<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>
```
* 创建一个网关分组和网关的限流规则
```java
@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
[sentinel dashboard](http://localhost:8748)
> java -Dserver.port=8748 -Dcsp.sentinel.dashboard.server=localhost:8748 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.1.jar
添加流控规则
![sentinel添加流控规则](images/snipaste_20211030_164359.png)
<br>
#### 2.6 搭建注册中心
<br>
[nacos](http://localhost:8848/nacos/#/login)
![nacos](images/snipaste_20211030_164909.png)
#### 2.7 搭建 zipkin server
[zipkin server](http://localhost:9411/zipkin/)
![](images/snipaste_20211030_175939.png)
<br>
![](images/snipaste_20211030_180038.png)
批处理 启动
```bat
@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
![](images/snipaste_20211101_112449.png)
```java
@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);
}
}
```
测试
![](images/snipaste_20211101_112721.png)
#### 2.8 搭建 管理后台
* mystyle-cloud-admin-manager
> 包结构 com.zhangmeng.admin.manager
![snipaste_20211101_145937](images/snipaste_20211101_145937.png)
#### 2.9 api 搭建 api
* mystyle-cloud-api
> 包结构 com.zhangmeng.api
>
![snipaste_20211101_145754](images/snipaste_20211101_145754.png)
```java
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;
}
}
```
[admin_manager](http://localhost:31003/swagger-ui.html)
![](images/snipaste_20211101_145433.png)
#### 2.10 搭建 canal
* mystyle-cloud-canal
> 包结构 com.zhangmeng.canal
![](images/snipaste_20211101_172449.png)
(2) 创建账号 用于测试使用,
使用root账号创建用户并授予权限
```properties
create user canal@'%' IDENTIFIED by 'canal';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT,SUPER ON *.* TO 'canal'@'%';
FLUSH PRIVILEGES;
```
```java
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
@EnableEurekaClient
@EnableCanalClient
public class CanalApplication {
public static void main(String[] args) {
SpringApplication.run(CanalApplication.class,args);
}
}
```
![canal](images/snipaste_20211102_151056.png)
#### 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
![Encoded password does not look like BCrypt](images/snipaste_20211104_104922.png)
![](images/snipaste_20211104_105055.png)
#### 2.13 搭建博客
* mystyle-cloud-blog
> 包结构 com.zhangmeng.blog
#### 2.14 搭建定时任务
思路
>* quartz + rabbit_mq
>
>* xxl-job 分布式调度框架
* mystyle-cloud-quartz
> com.zhangmeng.quartz
![snipaste_20211112_143604](images/snipaste_20211112_143604.png)
后续调整为:
* mystyle-cloud-xxl-job
> com.zhangmeng.job
引入依赖
```yaml
<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>
```
#### 2.15 搭建邮件
* mystyle-cloud-mail
> 包结构 com.zhangmeng.mail
```java
<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
```yaml
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
enabled: true
```
```xml
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
```
```java
- /actuator/**
- /instances/**
```
![springboot-admin-snipaste_20211117_153059](images/snipaste_20211117_153059.png)
![日志文件](images/snipaste_20211117_153239.png)
### 3.接口说明
#### 3.1 登录接口
##### 3.1.1 验证码
[验证码](http://localhost:9000/mystyle-cloud-admin-manager/verificationCode/generate)
![20211106_144846](images/snipaste_20211106_144846.png)
##### 3.1.2 查询当前用户
[地址](http://localhost:9000/mystyle-cloud-admin-manager/user/current)
##### 3.1.3 登录
* 问题 跨域
![](images/snipaste_20211106_154833.png)
[地址](http://localhost:9000/mystyle-cloud-oauth/user/login)
* 解决办法
gate_way yml 配置
```yaml
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
```
或者
```java
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);
}
}
```
> 提示 调用的具体服务去掉跨域配置
[首页访问](http://localhost:31007/login.html)
![login](images/snipaste_20211108_160302.png)
#### 3.2 首页
-------
>**获取鉴权信息**
<br>
* 源码
<br>
oauth2.0 获取鉴权
```java
/**
* 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) {
}
}
}
```
![snipaste_20211108_163411](images/snipaste_20211108_163411.png)
##### 3.2.1 先从请求头中获取
```java
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;
}
```
![](images/snipaste_20211108_163640.png)
```java
/**
* 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;
}
```
![](images/snipaste_20211108_163901.png)
##### 3.2.2 从请求参数中获取
```java
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;
}
```
![snipaste_20211108_165356](images/snipaste_20211108_165356.png)
-------------------------------------------
##### 3.2.3 问题
>首页不展示
![](images/snipaste_20211109_150805.png)
>控制台报错 Refused to display 'http://localhost:9000/' in a frame because it set 'X-Frame-Options' to 'deny'.
> 解决办法
```java
http.headers().frameOptions().disable();
```
![snipaste_20211109_151003](images/snipaste_20211109_151003.png)
--------------------------
![首页](images/snipaste_20211109_150329.png)
-------------------------------
> token 过期展示
![token过期](images/snipaste_20211109_162244.png)
#### 3.3 文件上传
![snipaste_20211110_141127](images/snipaste_20211110_141127.png)
> 解决办法
```yaml
spring:
servlet:
multipart:
max-file-size: 4GB
max-request-size: 4GB
```
![](images/snipaste_20211213_161208.png)
#### 3.4 feign 远程调用
> 报错 nested exception is feign.RetryableException: too many bytes written executing
>* 解决办法
>
```java
// 跳过 content-length
if (element.equals("content-length")){
continue;
}
```
```java
@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);
}
}
}
}
}
```
### 4. 博客展示
![](images/snipaste_20211213_160516.png)
#### 4.1 详情
![](images/snipaste_20211213_160628.png)
#### 4.2 关于我
![](images/snipaste_20211213_160806.png)
### 5.小说展示
![](images/snipaste_20211213_170811.png)
#### 5.2 小说展示
![](images/snipaste_20211213_170643.png)