未验证 提交 073d7db6 编写于 作者: zlt2000's avatar zlt2000 提交者: Gitee

!25 合并dev

Merge pull request !25 from zlt2000/dev
......@@ -42,5 +42,10 @@
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-websocket</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
......@@ -9,6 +9,7 @@ import org.springframework.security.config.annotation.web.configurers.Expression
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.authentication.TokenExtractor;
import org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler;
import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;
......@@ -36,13 +37,17 @@ public class DefaultResourceServerConf extends ResourceServerConfigurerAdapter {
@Autowired
private SecurityProperties securityProperties;
@Resource
private TokenExtractor tokenExtractor;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.tokenStore(tokenStore)
.stateless(true)
.authenticationEntryPoint(authenticationEntryPoint)
.expressionHandler(expressionHandler)
.accessDeniedHandler(oAuth2AccessDeniedHandler);
.accessDeniedHandler(oAuth2AccessDeniedHandler)
.tokenExtractor(tokenExtractor);
}
@Override
......
package com.central.oauth2.common.config;
import com.central.oauth2.common.util.AuthUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.websocket.server.ServerEndpointConfig;
/**
* webSocket鉴权配置
*
* @author zlt
* @version 1.0
* @date 2022/5/8
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Slf4j
public class WcAuthConfigurator extends ServerEndpointConfig.Configurator {
@Override
public boolean checkOrigin(String originHeaderValue) {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
try {
//检查token有效性
AuthUtils.checkAccessToken(servletRequestAttributes.getRequest());
} catch (Exception e) {
log.error("WebSocket-auth-error", e);
return false;
}
return super.checkOrigin(originHeaderValue);
}
}
package com.central.oauth2.common.service.impl;
import com.central.oauth2.common.properties.SecurityProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.authentication.BearerTokenExtractor;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
/**
* 自定义 TokenExtractor
*
* @author zlt
* @version 1.0
* @date 2022/6/4
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@ConditionalOnClass(HttpServletRequest.class)
@Component
public class CustomBearerTokenExtractor extends BearerTokenExtractor {
@Resource
private SecurityProperties securityProperties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
/**
* 解决只要请求携带access_token,排除鉴权的url依然会被拦截
*/
@Override
public Authentication extract(HttpServletRequest request) {
//判断当前请求为排除鉴权的url时,直接返回null
for (String url : securityProperties.getIgnore().getUrls()) {
if (antPathMatcher.match(url, request.getRequestURI())) {
return null;
}
}
return super.extract(request);
}
}
......@@ -3,11 +3,15 @@ package com.central.oauth2.common.util;
import com.central.common.constant.CommonConstant;
import com.central.common.constant.SecurityConstants;
import com.central.common.model.SysUser;
import com.central.common.utils.SpringUtil;
import com.central.oauth2.common.token.CustomWebAuthenticationDetails;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenStore;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
......@@ -66,6 +70,29 @@ public class AuthUtils {
return null;
}
/**
* 校验accessToken
*/
public static void checkAccessToken(HttpServletRequest request) {
String accessToken = extractToken(request);
checkAccessToken(accessToken);
}
public static void checkAccessToken(String accessTokenValue) {
TokenStore tokenStore = SpringUtil.getBean(TokenStore.class);
OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);
if (accessToken == null || accessToken.getValue() == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
} else if (accessToken.isExpired()) {
tokenStore.removeAccessToken(accessToken);
throw new InvalidTokenException("Access token expired: " + accessTokenValue);
}
OAuth2Authentication result = tokenStore.readAuthentication(accessToken);
if (result == null) {
throw new InvalidTokenException("Invalid access token: " + accessTokenValue);
}
}
/**
* *从header 请求中的clientId:clientSecret
*/
......
......@@ -2,6 +2,7 @@ package com.central.common.utils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
......@@ -11,6 +12,7 @@ import org.springframework.stereotype.Component;
* @author 作者 owen E-mail: 624191343@qq.com
*/
@Component
@Order(0)
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
......
......@@ -3,4 +3,5 @@ com.central.common.config.BannerInitializer
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.central.common.feign.fallback.UserServiceFallbackFactory,\
com.central.common.lock.LockAspect
\ No newline at end of file
com.central.common.lock.LockAspect,\
com.central.common.utils.SpringUtil
\ No newline at end of file
......@@ -40,7 +40,6 @@ public class MybatisPlusAutoConfigure {
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor mpInterceptor = new MybatisPlusInterceptor();
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
boolean enableTenant = tenantProperties.getEnable();
//是否开启多租户隔离
if (enableTenant) {
......@@ -48,6 +47,7 @@ public class MybatisPlusAutoConfigure {
tenantLineHandler, tenantProperties.getIgnoreSqls());
mpInterceptor.addInnerInterceptor(tenantInterceptor);
}
mpInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mpInterceptor;
}
......
......@@ -22,5 +22,7 @@
<module>sso-demo</module>
<!-- dubbo集成demo -->
<module>dubbo-demo</module>
<!-- webSocket集成demo -->
<module>websocket-demo</module>
</modules>
</project>
\ No newline at end of file
## 代码说明
- [SpringBoot的WebSocket接口如何实现鉴权?](https://www.kancloud.cn/zlt2000/microservices-platform/2278851)
&nbsp;
## 启动以下服务
1. zlt-uaa:统一认证中心
2. user-center:用户服务
3. sc-gateway:api网关
4. websocket-demo
> 环境配置与启动参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/919418
&nbsp;
## 获取access_token
可使用任意授权模式获取;
例如:密码模式授权
- 请求方式:POST
- 请求头:Authorization:Basic d2ViQXBwOndlYkFwcA==
- 请求地址:http://localhost:9900/api-uaa/oauth/token?grant_type=password&username=admin&password=admin
> 授权接口清单参考文档:https://www.kancloud.cn/zlt2000/microservices-platform/1158135
&nbsp;
## 测试步骤
使用 `Postman` 进行测试(最新版本支持 webSocket 接口)
点击 `New` 按钮,选择 `WebSocket Request`
`URL` 中输入 `ws://localhost:8092/websocket/test`
`Headers` 中添加:`Authorization:Bearer xxx`
或者
`参数` 中添加:`ws://localhost:8092/websocket/test?access_token=xxx`
> xxx 需替换为正确的 access_token
\ No newline at end of file
<?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>
<parent>
<groupId>com.zlt</groupId>
<artifactId>zlt-demo</artifactId>
<version>5.3.0</version>
</parent>
<artifactId>websocket-demo</artifactId>
<dependencies>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-config</artifactId>
</dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-auth-client-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.zlt</groupId>
<artifactId>zlt-redis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
\ No newline at end of file
package org.zlt;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author zlt
* @date 2022/5/8
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@SpringBootApplication
public class WebSocketApp {
public static void main(String[] args) {
SpringApplication.run(WebSocketApp.class, args);
}
}
package org.zlt.config;
import com.central.oauth2.common.config.DefaultResourceServerConf;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
/**
* security资源服务器配置
*
* @author zlt
* @version 1.0
* @date 2022/5/9
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Configuration
@EnableResourceServer
public class MyResourceConfig extends DefaultResourceServerConf {
}
package org.zlt.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket配置
*
* @author zlt
* @version 1.0
* @date 2022/5/8
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
package org.zlt.controller;
import com.central.oauth2.common.config.WcAuthConfigurator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
/**
* @author zlt
* @date 2022/5/8
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
@Slf4j
@Component
@ServerEndpoint(value = "/websocket/test", configurator = WcAuthConfigurator.class)
public class TestWebSocketController {
@OnOpen
public void onOpen(Session session) throws IOException {
session.getBasicRemote().sendText("TestWebSocketController-ok");
}
}
server:
port: 8092
spring:
application:
name: zlt-websocket
main:
allow-bean-definition-overriding: true
zlt:
security:
ignore:
httpUrls: >
/websocket/**
\ No newline at end of file
package com.central.gateway.auth;
import com.central.oauth2.common.properties.SecurityProperties;
import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* 自定义 ServerWebExchangeMatcher
* 解决只要请求携带access_token,排除鉴权的url依然会被拦截
*
* @author zlt
* @version 1.0
* @date 2022/6/10
* <p>
* Blog: https://zlt2000.gitee.io
* Github: https://github.com/zlt2000
*/
public class CustomServerWebExchangeMatchers implements ServerWebExchangeMatcher {
private final SecurityProperties securityProperties;
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
public CustomServerWebExchangeMatchers(SecurityProperties securityProperties) {
this.securityProperties = securityProperties;
}
@Override
public Mono<MatchResult> matches(ServerWebExchange exchange) {
for (String url : securityProperties.getIgnore().getUrls()) {
if (antPathMatcher.match(url, exchange.getRequest().getURI().getPath())) {
return MatchResult.notMatch();
}
}
return MatchResult.match();
}
}
......@@ -48,6 +48,7 @@ public class ResourceServerConfiguration {
oauth2Filter.setServerAuthenticationConverter(tokenAuthenticationConverter);
oauth2Filter.setAuthenticationFailureHandler(new ServerAuthenticationEntryPointFailureHandler(entryPoint));
oauth2Filter.setAuthenticationSuccessHandler(new Oauth2AuthSuccessHandler());
oauth2Filter.setRequiresAuthenticationMatcher(new CustomServerWebExchangeMatchers(securityProperties));
http.addFilterAt(oauth2Filter, SecurityWebFiltersOrder.AUTHENTICATION);
ServerHttpSecurity.AuthorizeExchangeSpec authorizeExchange = http.authorizeExchange();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册