-
Notifications
You must be signed in to change notification settings - Fork 622
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request !95 from 小灰熊/dev
- Loading branch information
Showing
3 changed files
with
191 additions
and
166 deletions.
There are no files selected for viewing
115 changes: 115 additions & 0 deletions
115
.../main/java/com/github/xiaoymin/knife4j/spring/gateway/filter/AbstractBasicAuthFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
/* | ||
* Copyright © 2017-2023 Knife4j([email protected]) | ||
* | ||
* 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 com.github.xiaoymin.knife4j.spring.gateway.filter; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Base64; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.regex.Pattern; | ||
|
||
import lombok.Getter; | ||
import lombok.extern.slf4j.Slf4j; | ||
|
||
/*** | ||
* 抽象basic认证过滤器 | ||
* | ||
* @since 1.9.0 | ||
* @author <a href="mailto:[email protected]">[email protected]</a> | ||
* 2019/02/02 19:57 | ||
*/ | ||
@Slf4j | ||
@Getter | ||
public abstract class AbstractBasicAuthFilter { | ||
|
||
public static final String BASIC = "Basic"; | ||
|
||
protected List<Pattern> urlFilters = null; | ||
|
||
protected AbstractBasicAuthFilter() { | ||
urlFilters = new ArrayList<>(); | ||
urlFilters.add(Pattern.compile(".*?/doc\\.html.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/v2/api-docs.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/v2/api-docs-ext.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/swagger-resources.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/swagger-resources/configuration/ui.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/swagger-resources/configuration/security.*", Pattern.CASE_INSENSITIVE)); | ||
// https://gitee.com/xiaoym/knife4j/issues/I6H8BE | ||
urlFilters.add(Pattern.compile(".*?/swagger-ui.*", Pattern.CASE_INSENSITIVE)); | ||
urlFilters.add(Pattern.compile(".*?/v3/api-docs.*", Pattern.CASE_INSENSITIVE)); | ||
} | ||
|
||
/** | ||
* 添加外部过滤规则,正则表达式 | ||
* | ||
* @param rule 外部自定义规则 | ||
*/ | ||
public void addRule(String rule) { | ||
this.urlFilters.add(Pattern.compile(rule, Pattern.CASE_INSENSITIVE)); | ||
} | ||
|
||
/** | ||
* 添加外部过滤规则,正则表达式 | ||
* | ||
* @param rules | ||
*/ | ||
public void addRule(Collection<String> rules) { | ||
if (rules != null && !rules.isEmpty()) { | ||
rules.forEach(this::addRule); | ||
} | ||
} | ||
|
||
/** | ||
* 判断是否匹配 | ||
* | ||
* @param uri | ||
* @return | ||
*/ | ||
protected boolean match(String uri) { | ||
// 考虑双斜杠的问题会绕过校验 | ||
if (uri != null) { | ||
// https://gitee.com/xiaoym/knife4j/issues/I4XDYE | ||
String newUri = uri.replaceAll("/+", "/"); | ||
for (Pattern pattern : getUrlFilters()) { | ||
if (pattern.matcher(newUri).matches()) { | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
/** | ||
* base64解码 | ||
* | ||
* @param source | ||
* @return | ||
*/ | ||
protected String decodeBase64(String source) { | ||
String decodeStr = null; | ||
if (source != null) { | ||
try { | ||
byte[] bytes = Base64.getDecoder().decode(source); | ||
decodeStr = new String(bytes); | ||
} catch (Exception e) { | ||
log.error(e.getMessage(), e); | ||
} | ||
} | ||
return decodeStr; | ||
} | ||
|
||
} |
105 changes: 0 additions & 105 deletions
105
...-starter/src/main/java/com/github/xiaoymin/knife4j/spring/gateway/filter/BasicFilter.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,81 +14,96 @@ | |
* limitations under the License. | ||
*/ | ||
|
||
|
||
package com.github.xiaoymin.knife4j.spring.gateway.filter.basic; | ||
|
||
import com.github.xiaoymin.knife4j.spring.gateway.conf.GlobalConstants; | ||
import com.github.xiaoymin.knife4j.spring.gateway.filter.BasicFilter; | ||
import lombok.Data; | ||
import java.util.Base64; | ||
|
||
import org.springframework.http.HttpHeaders; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.http.server.reactive.ServerHttpResponse; | ||
import org.springframework.web.server.ResponseStatusException; | ||
import org.springframework.web.server.ServerWebExchange; | ||
import org.springframework.web.server.WebFilter; | ||
import org.springframework.web.server.WebFilterChain; | ||
import reactor.core.publisher.Mono; | ||
import org.springframework.web.server.WebSession; | ||
|
||
import java.util.Base64; | ||
import com.github.xiaoymin.knife4j.spring.gateway.conf.GlobalConstants; | ||
import com.github.xiaoymin.knife4j.spring.gateway.filter.AbstractBasicAuthFilter; | ||
|
||
import lombok.Getter; | ||
import lombok.Setter; | ||
import reactor.core.publisher.Mono; | ||
|
||
/** | ||
* basic认证过滤器 | ||
* | ||
* @author :ZhRunXin 2023/5/4 0:51 | ||
* @email :[email protected] | ||
* @description:Security basic auth for gateway | ||
*/ | ||
@Data | ||
public class WebFluxSecurityBasicAuthFilter extends BasicFilter implements WebFilter { | ||
|
||
/*** | ||
* 是否开启basic验证,默认不开启 | ||
*/ | ||
private boolean enableBasicAuth = false; | ||
|
||
private String userName; | ||
|
||
private String password; | ||
|
||
@Override | ||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { | ||
if (enableBasicAuth) { | ||
// 只拦截Knife4J资源 | ||
if (match(exchange.getRequest().getURI().toString())) { | ||
return exchange.getSession().doOnNext(session -> { | ||
Object attribute = session.getAttribute(GlobalConstants.KNIFE4J_BASIC_AUTH_SESSION); | ||
if (attribute != null) { | ||
return; | ||
} else { | ||
String authorization = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); | ||
ServerHttpResponse response = exchange.getResponse(); | ||
if (authorization == null) { | ||
writeForbiddenCode(response); | ||
} else { | ||
String[] parts = authorization.split(" "); | ||
if (parts.length != 2 || !parts[0].equals("Basic")) { | ||
writeForbiddenCode(response); | ||
} else { | ||
String credentials = new String(Base64.getDecoder().decode(parts[1])); | ||
String[] usernameAndPassword = credentials.split(":"); | ||
if (usernameAndPassword.length != 2 || !usernameAndPassword[0].equals(userName) || !usernameAndPassword[1].equals(password)) { | ||
writeForbiddenCode(response); | ||
} else { | ||
exchange.getSession().doOnNext(session1 -> { | ||
session1.getAttributes().put(GlobalConstants.KNIFE4J_BASIC_AUTH_SESSION, userName); | ||
}).subscribe(); | ||
} | ||
} | ||
} | ||
} | ||
}).then(chain.filter(exchange)); | ||
} | ||
} | ||
return chain.filter(exchange); | ||
} | ||
|
||
private void writeForbiddenCode(ServerHttpResponse serverHttpResponse) { | ||
serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED); | ||
serverHttpResponse.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Area\""); | ||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); | ||
} | ||
|
||
@Setter | ||
@Getter | ||
public class WebFluxSecurityBasicAuthFilter extends AbstractBasicAuthFilter implements WebFilter { | ||
|
||
/*** | ||
* 是否开启basic验证,默认不开启 | ||
*/ | ||
private boolean enableBasicAuth = false; | ||
|
||
private String userName; | ||
|
||
private String password; | ||
|
||
@Override | ||
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) { | ||
// 只拦截Knife4J资源 | ||
if (this.enableBasicAuth && this.match(exchange.getRequest().getURI().toString())) { | ||
return exchange.getSession().doOnNext(session -> this.doFilter(exchange, session)) | ||
.then(chain.filter(exchange)); | ||
} | ||
return chain.filter(exchange); | ||
} | ||
|
||
/** | ||
* 过滤处理 | ||
* | ||
* @param exchange | ||
* @param session | ||
*/ | ||
private void doFilter(ServerWebExchange exchange, WebSession session) { | ||
Object attribute = session.getAttribute(GlobalConstants.KNIFE4J_BASIC_AUTH_SESSION); | ||
if (attribute != null) { | ||
return; | ||
} | ||
String authorization = exchange.getRequest().getHeaders().getFirst(HttpHeaders.AUTHORIZATION); | ||
ServerHttpResponse response = exchange.getResponse(); | ||
if (authorization == null) { | ||
writeForbiddenCode(response); | ||
} | ||
|
||
String[] parts = authorization.split(" "); | ||
// 验证是否符合规则 | ||
if (parts.length != 2 || !parts[0].equals(BASIC)) { | ||
writeForbiddenCode(response); | ||
} | ||
|
||
String credentials = new String(Base64.getDecoder().decode(parts[1])); | ||
String[] usernameAndPassword = credentials.split(":"); | ||
// 验证用户名密码是否匹配 | ||
if (usernameAndPassword.length != 2 || !usernameAndPassword[0].equals(this.userName) | ||
|| !usernameAndPassword[1].equals(this.password)) { | ||
writeForbiddenCode(response); | ||
} else { | ||
exchange.getSession().doOnNext( | ||
session1 -> session1.getAttributes().put(GlobalConstants.KNIFE4J_BASIC_AUTH_SESSION, this.userName)) | ||
.subscribe(); | ||
} | ||
} | ||
|
||
private void writeForbiddenCode(ServerHttpResponse serverHttpResponse) { | ||
serverHttpResponse.setStatusCode(HttpStatus.UNAUTHORIZED); | ||
serverHttpResponse.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Area\""); | ||
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED); | ||
} | ||
|
||
} |