mirror of
https://gitee.com/republicline/rax-remote-v2.git
synced 2025-08-23 23:44:58 +08:00
parent
b4c11c9663
commit
8d176fc965
|
@ -1,5 +1,6 @@
|
|||
package com.rax.auth.config;
|
||||
|
||||
import com.rax.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.rax.auth.support.CustomeOAuth2AccessTokenGenerator;
|
||||
import com.rax.auth.support.core.CustomeOAuth2TokenCustomizer;
|
||||
import com.rax.auth.support.core.RaxDaoAuthenticationProvider;
|
||||
|
@ -37,11 +38,7 @@ import org.springframework.security.web.authentication.AuthenticationFailureHand
|
|||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
|
||||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
|
||||
@Configuration
|
||||
|
@ -60,51 +57,52 @@ public class AuthorizationServerConfiguration {
|
|||
|
||||
private final RaxLoginPreFilter raxLoginPreFilter;
|
||||
|
||||
private final RemoteClientDetailsService clientDetailsService;
|
||||
|
||||
@Bean
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
|
||||
RaxAuthenticationSuccessEventHandler successEventHandler,
|
||||
RaxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
RaxAuthenticationSuccessEventHandler successEventHandler,
|
||||
RaxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
|
||||
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
|
||||
|
||||
http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()));
|
||||
|
||||
http.addFilterAfter(raxLoginPreFilter, UsernamePasswordAuthenticationFilter.class);
|
||||
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
|
||||
Customizer.withDefaults());
|
||||
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
|
||||
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器
|
||||
.errorResponseHandler(failureEventHandler);// 登录失败处理器
|
||||
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
|
||||
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
|
||||
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
|
||||
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
|
||||
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getUrls()
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[] {});
|
||||
.stream()
|
||||
.map(AntPathRequestMatcher::new)
|
||||
.toList()
|
||||
.toArray(new AntPathRequestMatcher[]{});
|
||||
|
||||
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(raxBearerTokenExtractor))
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
.permitAll()
|
||||
.anyRequest()
|
||||
.authenticated())
|
||||
.oauth2ResourceServer(
|
||||
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
|
||||
.authenticationEntryPoint(resourceAuthExceptionEntryPoint)
|
||||
.bearerTokenResolver(raxBearerTokenExtractor))
|
||||
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint))
|
||||
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
|
||||
.csrf(AbstractHttpConfigurer::disable);
|
||||
|
||||
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
|
||||
.authorizationServerSettings(
|
||||
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
|
||||
Customizer.withDefaults());
|
||||
|
||||
DefaultSecurityFilterChain securityFilterChain = http.build();
|
||||
|
||||
// 注入自定义授权模式实现
|
||||
|
@ -115,6 +113,7 @@ public class AuthorizationServerConfiguration {
|
|||
/**
|
||||
* 令牌生成规则实现 </br>
|
||||
* client:username:uuid
|
||||
*
|
||||
* @return OAuth2TokenGenerator
|
||||
*/
|
||||
@Bean
|
||||
|
@ -137,6 +136,7 @@ public class AuthorizationServerConfiguration {
|
|||
|
||||
/**
|
||||
* request -> xToken 注入请求转换器
|
||||
*
|
||||
* @return DelegatingAuthenticationConverter
|
||||
*/
|
||||
private AuthenticationConverter accessTokenRequestConverter() {
|
||||
|
@ -160,10 +160,10 @@ public class AuthorizationServerConfiguration {
|
|||
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
|
||||
|
||||
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
|
||||
|
||||
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator());
|
||||
authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
|
||||
|
||||
// 处理 UsernamePasswordAuthenticationToken
|
||||
http.authenticationProvider(new RaxDaoAuthenticationProvider());
|
||||
|
@ -173,17 +173,4 @@ public class AuthorizationServerConfiguration {
|
|||
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider);
|
||||
}
|
||||
|
||||
@Bean
|
||||
CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
CorsConfiguration configuration = new CorsConfiguration();
|
||||
configuration.setAllowCredentials(true);
|
||||
configuration.addAllowedOriginPattern("*");
|
||||
configuration.setAllowedMethods(Arrays.asList("*"));
|
||||
configuration.setAllowedHeaders(Arrays.asList("*"));
|
||||
configuration.setMaxAge(Duration.ofHours(1));
|
||||
source.registerCorsConfiguration("/static/**", configuration);
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.rax.auth.endpoint;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.TemporalAccessorUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
|
@ -170,6 +171,9 @@ public class RaxTokenEndpoint {
|
|||
cacheManager.getCache(CacheConstants.USER_DETAILS).evictIfPresent(authorization.getPrincipalName());
|
||||
// 清空access token
|
||||
authorizationService.remove(authorization);
|
||||
String username = Base64.encode(authorization.getPrincipalName());
|
||||
redisTemplate.delete("rax_logged_in::" + username + "::a::");
|
||||
redisTemplate.delete("rax_logged_in::" + username + "::c::");
|
||||
// 处理自定义退出事件,保存相关日志
|
||||
SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
|
||||
authorization.getPrincipalName(), authorization.getRegisteredClientId())));
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
package com.rax.auth.support.base;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import com.rax.admin.api.entity.SysOauthClientDetails;
|
||||
import com.rax.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.rax.common.core.constant.SecurityConstants;
|
||||
import com.rax.common.core.util.R;
|
||||
import com.rax.common.core.util.RedisUtils;
|
||||
import com.rax.common.core.util.SpringContextHolder;
|
||||
import com.rax.common.security.service.RaxUser;
|
||||
import com.rax.common.security.util.OAuth2ErrorCodesExpand;
|
||||
import com.rax.common.security.util.ScopeException;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.support.MessageSourceAccessor;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.security.authentication.*;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.AuthenticationException;
|
||||
|
@ -23,17 +34,17 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke
|
|||
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* @author jumuning
|
||||
* @description
|
||||
*
|
||||
* 处理自定义授权
|
||||
* @description 处理自定义授权
|
||||
*/
|
||||
public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OAuth2ResourceOwnerBaseAuthenticationToken>
|
||||
implements AuthenticationProvider {
|
||||
|
@ -53,21 +64,29 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
@Deprecated
|
||||
private Supplier<String> refreshTokenGenerator;
|
||||
|
||||
private static final String LOGGED_IN = "rax_logged_in::";
|
||||
|
||||
private final RemoteClientDetailsService clientDetailsService;
|
||||
|
||||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
*
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
|
||||
RemoteClientDetailsService clientDetailsService
|
||||
) {
|
||||
Assert.notNull(authorizationService, "authorizationService cannot be null");
|
||||
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
|
||||
this.authenticationManager = authenticationManager;
|
||||
this.authorizationService = authorizationService;
|
||||
this.tokenGenerator = tokenGenerator;
|
||||
this.clientDetailsService = clientDetailsService;
|
||||
|
||||
// 国际化配置
|
||||
this.messages = new MessageSourceAccessor(SpringUtil.getBean("securityMessageSource"), Locale.CHINA);
|
||||
|
@ -83,6 +102,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
|
||||
/**
|
||||
* 当前provider是否支持此令牌类型
|
||||
*
|
||||
* @param authentication
|
||||
* @return
|
||||
*/
|
||||
|
@ -91,6 +111,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
|
||||
/**
|
||||
* 当前的请求客户端是否支持此模式
|
||||
*
|
||||
* @param registeredClient
|
||||
*/
|
||||
public abstract void checkClient(RegisteredClient registeredClient);
|
||||
|
@ -98,6 +119,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
/**
|
||||
* Performs authentication with the same contract as
|
||||
* {@link AuthenticationManager#authenticate(Authentication)} .
|
||||
*
|
||||
* @param authentication the authentication request object.
|
||||
* @return a fully authenticated object including credentials. May return
|
||||
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support
|
||||
|
@ -126,8 +148,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
}
|
||||
}
|
||||
authorizedScopes = new LinkedHashSet<>(resouceOwnerBaseAuthentication.getScopes());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
authorizedScopes = new LinkedHashSet<>();
|
||||
}
|
||||
|
||||
|
@ -139,7 +160,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
|
||||
|
||||
Authentication usernamePasswordAuthentication = authenticationManager
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
.authenticate(usernamePasswordAuthenticationToken);
|
||||
|
||||
// @formatter:off
|
||||
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
|
||||
|
@ -152,11 +173,11 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
// @formatter:on
|
||||
|
||||
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
|
||||
.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes);
|
||||
.withRegisteredClient(registeredClient)
|
||||
.principalName(usernamePasswordAuthentication.getName())
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD)
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes);
|
||||
|
||||
// ----- Access token -----
|
||||
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
|
||||
|
@ -171,29 +192,27 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
|
||||
if (generatedAccessToken instanceof ClaimAccessor) {
|
||||
authorizationBuilder.id(accessToken.getTokenValue())
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
}
|
||||
else {
|
||||
.token(accessToken,
|
||||
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
|
||||
((ClaimAccessor) generatedAccessToken).getClaims()))
|
||||
// 0.4.0 新增的方法
|
||||
.authorizedScopes(authorizedScopes)
|
||||
.attribute(Principal.class.getName(), usernamePasswordAuthentication);
|
||||
} else {
|
||||
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
|
||||
}
|
||||
|
||||
// ----- Refresh token -----
|
||||
OAuth2RefreshToken refreshToken = null;
|
||||
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) &&
|
||||
// Do not issue refresh token to public client
|
||||
// Do not issue refresh token to public client
|
||||
!clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
|
||||
|
||||
if (this.refreshTokenGenerator != null) {
|
||||
Instant issuedAt = Instant.now();
|
||||
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
|
||||
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
|
||||
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
|
||||
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
|
||||
|
@ -208,6 +227,8 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
|
||||
OAuth2Authorization authorization = authorizationBuilder.build();
|
||||
|
||||
checkIsLogin(authorization);
|
||||
|
||||
this.authorizationService.save(authorization);
|
||||
|
||||
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
|
||||
|
@ -215,8 +236,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
|
||||
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
|
||||
|
||||
}
|
||||
catch (Exception ex) {
|
||||
} catch (Exception ex) {
|
||||
LOGGER.error("problem in authenticate", ex);
|
||||
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
|
||||
}
|
||||
|
@ -225,15 +245,16 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
|
||||
/**
|
||||
* 登录异常转换为oauth2异常
|
||||
* @param authentication 身份验证
|
||||
*
|
||||
* @param authentication 身份验证
|
||||
* @param authenticationException 身份验证异常
|
||||
* @return {@link OAuth2AuthenticationException}
|
||||
*/
|
||||
private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication,
|
||||
AuthenticationException authenticationException) {
|
||||
AuthenticationException authenticationException) {
|
||||
if (authenticationException instanceof UsernameNotFoundException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND,
|
||||
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[] { authentication.getName() },
|
||||
this.messages.getMessage("JdbcDaoImpl.notFound", new Object[]{authentication.getName()},
|
||||
"Username {0} not found"),
|
||||
""));
|
||||
}
|
||||
|
@ -244,7 +265,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
}
|
||||
if (authenticationException instanceof LockedException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_LOCKED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"), ""));
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.locked", "User account is locked"), ""));
|
||||
}
|
||||
if (authenticationException instanceof DisabledException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE,
|
||||
|
@ -253,7 +274,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
}
|
||||
if (authenticationException instanceof AccountExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_EXPIRED, this.messages
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"), ""));
|
||||
.getMessage("AbstractUserDetailsAuthenticationProvider.expired", "User account has expired"), ""));
|
||||
}
|
||||
if (authenticationException instanceof CredentialsExpiredException) {
|
||||
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.CREDENTIALS_EXPIRED,
|
||||
|
@ -284,4 +305,23 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
|
|||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新登录用户会把老登录用户给踢下线
|
||||
*/
|
||||
private void checkIsLogin(OAuth2Authorization authorization) {
|
||||
String username = Base64.encode(authorization.getPrincipalName());
|
||||
RedisTemplate<String, String> redisTemplate = SpringContextHolder.getBean(RedisTemplate.class);
|
||||
String previousAccessToken = redisTemplate.opsForValue().get(LOGGED_IN + username + "::a::");
|
||||
String previousRefreshToken = redisTemplate.opsForValue().get(LOGGED_IN + username + "::c::");
|
||||
String accessToken = authorization.getAccessToken().getToken().getTokenValue();
|
||||
String refreshToken = authorization.getRefreshToken().getToken().getTokenValue();
|
||||
if (StringUtils.hasText(previousAccessToken)) {
|
||||
redisTemplate.delete("token::access_token::" + previousAccessToken);
|
||||
redisTemplate.delete("token::refresh_token::" + previousRefreshToken);
|
||||
}
|
||||
R<SysOauthClientDetails> clientDetailsById = clientDetailsService.getClientDetailsById(authorization.getRegisteredClientId(), SecurityConstants.FROM_IN);
|
||||
redisTemplate.opsForValue().set(LOGGED_IN + username + "::a::", accessToken, clientDetailsById.getData().getAccessTokenValidity(), TimeUnit.SECONDS);
|
||||
redisTemplate.opsForValue().set(LOGGED_IN + username + "::c::", refreshToken, clientDetailsById.getData().getRefreshTokenValidity(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.rax.auth.support.password;
|
||||
|
||||
import com.rax.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
@ -28,15 +29,17 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider
|
|||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
*
|
||||
* @param authenticationManager
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
super(authenticationManager, authorizationService, tokenGenerator);
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
|
||||
RemoteClientDetailsService clientDetailsService) {
|
||||
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.rax.auth.support.sms;
|
||||
|
||||
import com.rax.admin.api.feign.RemoteClientDetailsService;
|
||||
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
|
||||
import com.rax.common.core.constant.SecurityConstants;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
@ -19,7 +20,7 @@ import java.util.Map;
|
|||
/**
|
||||
* @author lengleng
|
||||
* @date date
|
||||
*
|
||||
* <p>
|
||||
* 短信登录的核心处理
|
||||
*/
|
||||
public class OAuth2ResourceOwnerSmsAuthenticationProvider
|
||||
|
@ -30,15 +31,17 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
|
|||
/**
|
||||
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
|
||||
* provided parameters.
|
||||
*
|
||||
* @param authenticationManager
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @param authorizationService the authorization service
|
||||
* @param tokenGenerator the token generator
|
||||
* @since 0.2.3
|
||||
*/
|
||||
public OAuth2ResourceOwnerSmsAuthenticationProvider(AuthenticationManager authenticationManager,
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) {
|
||||
super(authenticationManager, authorizationService, tokenGenerator);
|
||||
OAuth2AuthorizationService authorizationService,
|
||||
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
|
||||
RemoteClientDetailsService clientDetailsService) {
|
||||
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +55,7 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
|
|||
public void checkClient(RegisteredClient registeredClient) {
|
||||
assert registeredClient != null;
|
||||
if (!registeredClient.getAuthorizationGrantTypes()
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.MOBILE))) {
|
||||
.contains(new AuthorizationGrantType(SecurityConstants.MOBILE))) {
|
||||
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,6 @@ import java.util.*;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2020-03-11
|
||||
* <p>
|
||||
* 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理
|
||||
|
|
|
@ -59,6 +59,11 @@ public class OpenAPIDefinition extends OpenAPI implements InitializingBean, Appl
|
|||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param swaggerProperties
|
||||
* @return
|
||||
*/
|
||||
private SecurityScheme securityScheme(SwaggerProperties swaggerProperties) {
|
||||
OAuthFlow clientCredential = new OAuthFlow();
|
||||
clientCredential.setTokenUrl(swaggerProperties.getTokenUrl());
|
||||
|
|
|
@ -17,11 +17,11 @@
|
|||
<modules>
|
||||
<module>common-bom</module>
|
||||
<module>common-core</module>
|
||||
<module>common-datasource</module>
|
||||
<!--<module>common-datasource</module>-->
|
||||
<module>common-log</module>
|
||||
<module>common-mybatis</module>
|
||||
<module>common-oss</module>
|
||||
<module>common-seata</module>
|
||||
<!--<module>common-seata</module>-->
|
||||
<module>common-security</module>
|
||||
<module>common-feign</module>
|
||||
<module>common-swagger</module>
|
||||
|
|
|
@ -623,6 +623,8 @@ CREATE TABLE `sys_user` (
|
|||
`wx_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信登录openId',
|
||||
`mini_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '小程序openId',
|
||||
`qq_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT 'QQ openId',
|
||||
`sex` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '性别',
|
||||
`hospital_id` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '医院id',
|
||||
PRIMARY KEY (`user_id`) USING BTREE,
|
||||
KEY `user_wx_openid` (`wx_openid`) USING BTREE,
|
||||
KEY `user_qq_openid` (`qq_openid`) USING BTREE,
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -89,7 +89,7 @@
|
|||
|
||||
<modules>
|
||||
<!-- 代码生成模块 -->
|
||||
<module>codegen</module>
|
||||
<!--<module>codegen</module>-->
|
||||
<!-- Quartz模块 -->
|
||||
<module>quartz</module>
|
||||
<!-- 身份验证模块 -->
|
||||
|
|
|
@ -36,7 +36,7 @@ public class SysLog implements Serializable {
|
|||
* 日志类型
|
||||
*/
|
||||
@NotBlank(message = "日志类型不能为空")
|
||||
@ExcelProperty("日志类型(0-正常 9-错误)")
|
||||
@ExcelProperty("日志类型(0-正常 9-错误 1-添加 2-删除 3-编辑)")
|
||||
@Schema(description = "日志类型")
|
||||
private String logType;
|
||||
|
||||
|
|
|
@ -148,6 +148,6 @@ public class SysUser implements Serializable {
|
|||
private String sex;
|
||||
|
||||
@Schema(description = "医院")
|
||||
private String hospital;
|
||||
private String hospitalId;
|
||||
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ public class SysHospitalController {
|
|||
return sysHospitalService.getHospitalList(name, offset, limit);
|
||||
}
|
||||
|
||||
@RequestMapping("/getHospitalList")
|
||||
@PostMapping("/getHospitalList")
|
||||
R<List> getHospitalList(String name) {
|
||||
return sysHospitalService.getHospitalList(name);
|
||||
}
|
||||
|
|
|
@ -39,7 +39,8 @@ public class SysLogController {
|
|||
|
||||
/**
|
||||
* 简单分页查询
|
||||
* @param page 分页对象
|
||||
*
|
||||
* @param page 分页对象
|
||||
* @param sysLog 系统日志
|
||||
* @return
|
||||
*/
|
||||
|
@ -50,6 +51,7 @@ public class SysLogController {
|
|||
|
||||
/**
|
||||
* 批量删除日志
|
||||
*
|
||||
* @param ids ID
|
||||
* @return success/false
|
||||
*/
|
||||
|
@ -61,6 +63,7 @@ public class SysLogController {
|
|||
|
||||
/**
|
||||
* 插入日志
|
||||
*
|
||||
* @param sysLog 日志实体
|
||||
* @return success/false
|
||||
*/
|
||||
|
@ -72,6 +75,7 @@ public class SysLogController {
|
|||
|
||||
/**
|
||||
* 导出excel 表格
|
||||
*
|
||||
* @param sysLog 查询条件
|
||||
* @return
|
||||
*/
|
||||
|
@ -82,4 +86,9 @@ public class SysLogController {
|
|||
return sysLogService.list(Wrappers.query(sysLog));
|
||||
}
|
||||
|
||||
@PostMapping("/getMonthlyLogCount")
|
||||
public R getMonthlyLogCount(String startTime, String endTime) {
|
||||
return R.ok(sysLogService.getMonthlyLogCount(startTime, endTime));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -3,16 +3,17 @@ package com.rax.admin.mapper;
|
|||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.rax.admin.api.entity.SysLog;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 日志表 Mapper 接口
|
||||
* </p>
|
||||
*
|
||||
* @author lengleng
|
||||
* @since 2017-11-20
|
||||
*/
|
||||
@Mapper
|
||||
public interface SysLogMapper extends BaseMapper<SysLog> {
|
||||
|
||||
List<Map> getMonthlyLogCount(@Param("startTime") String startTime, @Param("endTime") String endTime);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
|
|||
import com.rax.admin.api.dto.SysLogDTO;
|
||||
import com.rax.admin.api.entity.SysLog;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 日志表 服务类
|
||||
|
@ -30,4 +33,6 @@ public interface SysLogService extends IService<SysLog> {
|
|||
*/
|
||||
Boolean saveLog(SysLog sysLog);
|
||||
|
||||
List<Map> getMonthlyLogCount(String startTime, String endTime);
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,9 @@ import com.rax.admin.service.SysLogService;
|
|||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 日志表 服务实现类
|
||||
|
@ -52,4 +55,8 @@ public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> impleme
|
|||
return Boolean.TRUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map> getMonthlyLogCount(String startTime, String endTime) {
|
||||
return baseMapper.getMonthlyLogCount(startTime, endTime);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
server:
|
||||
port: 9999
|
||||
port: 8888
|
||||
servlet:
|
||||
context-path: /admin
|
||||
|
||||
|
@ -17,7 +17,7 @@ spring:
|
|||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: root
|
||||
url: jdbc:mysql://192.168.65.130:3306/rax_backend?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
|
||||
url: jdbc:mysql://192.168.65.130:3306/rax_backend?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&allowMultiQueries=true&nullCatalogMeansCurrent=true
|
||||
# 定时任务属性配置
|
||||
quartz:
|
||||
properties:
|
||||
|
@ -46,6 +46,7 @@ spring:
|
|||
multipart:
|
||||
max-file-size: 100MB
|
||||
max-request-size: 100MB
|
||||
# 静态资源文件
|
||||
mvc:
|
||||
static-path-pattern: /static/**
|
||||
|
||||
|
@ -79,10 +80,6 @@ security:
|
|||
- /rax/**
|
||||
- /hospital/getHospitalList
|
||||
# 临时白名单
|
||||
- /static/**
|
||||
# - /topic/**
|
||||
# - /front/**
|
||||
# - /medicine/**
|
||||
|
||||
|
||||
#--------------如下配置尽量不要变动-------------
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.rax.admin.mapper.SysLogMapper">
|
||||
<select id="getMonthlyLogCount" resultType="java.util.Map">
|
||||
SELECT
|
||||
date_format( create_time,'%Y-%m-%d') "date", COUNT(*) "count"
|
||||
FROM `sys_log`
|
||||
WHERE
|
||||
create_time <![CDATA[ >= ]]> #{startTime} AND create_time <![CDATA[ <= ]]> #{endTime}
|
||||
GROUP BY date_format(create_time,'%Y-%m-%d')
|
||||
</select>
|
||||
</mapper>
|
|
@ -1,93 +0,0 @@
|
|||
const stompClient = new StompJs.Client({
|
||||
brokerURL: 'ws://localhost:9999/admin/rax/SurgeryData'
|
||||
});
|
||||
/*const testClient = new StompJs.Client({
|
||||
brokerURL: "ws://localhost:8080/test-guide"
|
||||
})*/
|
||||
|
||||
let user = ""
|
||||
|
||||
stompClient.onConnect = (frame) => {
|
||||
setConnected(true);
|
||||
console.log('Connected: ' + frame);
|
||||
/*stompClient.subscribe('/topic/user/'+user+'/doctorMedicine', (greeting) => {
|
||||
showGreeting(greeting.body);
|
||||
});*/
|
||||
};
|
||||
|
||||
/*testClient.onConnect = (data) => {
|
||||
console.log('Connected: ' + data);
|
||||
testClient.subscribe('/topic/test', (greeting) => {
|
||||
console.log(JSON.parse(greeting.body).content);
|
||||
});
|
||||
}*/
|
||||
|
||||
stompClient.onWebSocketError = (error) => {
|
||||
console.error('Error with websocket', error);
|
||||
};
|
||||
|
||||
stompClient.onStompError = (frame) => {
|
||||
console.error('Broker reported error: ' + frame.headers['message']);
|
||||
console.error('Additional details: ' + frame.body);
|
||||
};
|
||||
|
||||
function setConnected(connected) {
|
||||
$("#connect").prop("disabled", connected);
|
||||
$("#disconnect").prop("disabled", !connected);
|
||||
if (connected) {
|
||||
$("#conversation").show();
|
||||
}
|
||||
else {
|
||||
$("#conversation").hide();
|
||||
}
|
||||
$("#greetings").html("");
|
||||
}
|
||||
|
||||
function connect() {
|
||||
stompClient.activate();
|
||||
// testClient.activate();
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
sendNameToServer('stop');
|
||||
stompClient.deactivate();
|
||||
// testClient.deactivate();
|
||||
setConnected(false);
|
||||
console.log("Disconnected");
|
||||
}
|
||||
|
||||
function sendName() {
|
||||
user = Math.random()
|
||||
$("#user-id").val(user)
|
||||
sendNameToServer();
|
||||
}
|
||||
|
||||
function sendNameToServer(status) {
|
||||
/*stompClient.publish({
|
||||
destination: "/front/doctorMedicine",
|
||||
body: status ? status : $("#name").val()
|
||||
});*/
|
||||
stompClient.publish({
|
||||
destination: "/front/doctorMedicine",
|
||||
body: JSON.stringify({'status': status, "db": "test", user})
|
||||
});
|
||||
stompClient.subscribe('/topic/user/'+user+'/doctorMedicine', (greeting) => {
|
||||
showGreeting(greeting.body);
|
||||
});
|
||||
/*testClient.publish({
|
||||
destination: "/app/test-hello",
|
||||
body: JSON.stringify({'name': "test-hello"})
|
||||
})*/
|
||||
}
|
||||
|
||||
function showGreeting(message) {
|
||||
$("#greetings").append("<tr><td>" + message + "</td></tr>");
|
||||
}
|
||||
|
||||
$(function () {
|
||||
$("form").on('submit', (e) => e.preventDefault());
|
||||
$( "#connect" ).click(() => connect());
|
||||
$( "#disconnect" ).click(() => disconnect());
|
||||
$( "#send" ).click(() => sendName());
|
||||
});
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello WebSocket</title>
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link href="/admin/static/html/main.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@stomp/stompjs@7.0.0/bundles/stomp.umd.min.js"></script>
|
||||
<script src="/admin/static/html/app.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
|
||||
enabled. Please enable
|
||||
Javascript and reload this page!</h2></noscript>
|
||||
<div id="main-content" class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="connect">WebSocket connection:</label>
|
||||
<button id="connect" class="btn btn-default" type="submit">Connect</button>
|
||||
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<form class="form-inline">
|
||||
<div class="form-group">
|
||||
<label for="name">What is your name?</label>
|
||||
<input type="text" id="name" class="form-control" placeholder="Your name here...">
|
||||
<span id="user-id"></span>
|
||||
</div>
|
||||
<button id="send" class="btn btn-default" type="submit">Send</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table id="conversation" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Greetings</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="greetings">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -1,14 +0,0 @@
|
|||
body {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
#main-content {
|
||||
max-width: 940px;
|
||||
padding: 2em 3em;
|
||||
margin: 0 auto 20px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #e5e5e5;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB |
Binary file not shown.
Before Width: | Height: | Size: 35 KiB |
Binary file not shown.
Before Width: | Height: | Size: 6.3 KiB |
|
@ -44,17 +44,15 @@ public class WSChannelInterceptor implements ChannelInterceptor {
|
|||
String token = accessToken.get(0);
|
||||
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
|
||||
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
|
||||
if (StompCommand.CONNECT.equals(accessor.getCommand()) || StompCommand.SEND.equals(accessor.getCommand())) {
|
||||
if (authorization == null) {
|
||||
throw new AccessDeniedException("Access is denied");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (StompCommand.ABORT.equals(accessor.getCommand())) {
|
||||
System.out.println("StompCommand.ABORT");
|
||||
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())
|
||||
|| StompCommand.UNSUBSCRIBE.equals(accessor.getCommand())) {
|
||||
if (StompCommand.DISCONNECT.equals(accessor.getCommand())
|
||||
|| StompCommand.UNSUBSCRIBE.equals(accessor.getCommand()) || StompCommand.ABORT.equals(accessor.getCommand())) {
|
||||
String simpSessionId = (String) accessor.getHeader("simpSessionId");
|
||||
vitalSignTimer.stopTimerTaskMongo(simpSessionId);
|
||||
chatService.stopTimerTaskMongo(simpSessionId);
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.rax.vital.medicine.controller;
|
|||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rax.vital.medicine.service.ChatService;
|
||||
import com.rax.vital.util.DatabaseNameUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
|
@ -11,6 +12,8 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat
|
|||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@Controller
|
||||
public class ChatController {
|
||||
|
||||
|
@ -27,9 +30,16 @@ public class ChatController {
|
|||
if (authorization != null) {
|
||||
String username = authorization.getPrincipalName();
|
||||
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
|
||||
String db = params.getString("db");
|
||||
// 病人名
|
||||
String patientName = params.getString("patientName");
|
||||
// 病人身份证
|
||||
String idNum = params.getString("idNum");
|
||||
// yyyyMMdd
|
||||
String date = params.getString("date");
|
||||
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
|
||||
// 消息内容
|
||||
String msg = params.getString("msg");
|
||||
chatService.sendMessage(db, username, simpSessionId, msg);
|
||||
chatService.sendMessage(databaseName, username, simpSessionId, msg);
|
||||
} else {
|
||||
throw new AccessDeniedException("Access is denied");
|
||||
}
|
||||
|
|
|
@ -2,17 +2,21 @@ package com.rax.vital.medicine.controller;
|
|||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.rax.vital.timer.VitalSignTimer;
|
||||
import com.rax.vital.util.DatabaseNameUtil;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
|
||||
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用药
|
||||
|
@ -32,14 +36,44 @@ public class MedicineController {
|
|||
|
||||
@MessageMapping("/getSurgeryData")
|
||||
public void doctorMedicine(MessageHeaders messageHeaders, String body) {
|
||||
LinkedMultiValueMap nativeHeaders = (LinkedMultiValueMap) messageHeaders.get("nativeHeaders");
|
||||
ArrayList tokenList = (ArrayList) nativeHeaders.get("token");
|
||||
String token = (String) tokenList.get(0);
|
||||
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
|
||||
String username = authorization.getPrincipalName();
|
||||
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
|
||||
|
||||
JSONObject params = JSONObject.parseObject(body);
|
||||
OAuth2Authorization authorization = authorizationService.findByToken(params.getString("token"), OAuth2TokenType.ACCESS_TOKEN);
|
||||
if (authorization != null) {
|
||||
String username = authorization.getPrincipalName();
|
||||
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
|
||||
vitalSignTimer.createAndSendMessageMongo(params.getString("db"), username, simpSessionId);
|
||||
} else {
|
||||
throw new AccessDeniedException("Access is denied");
|
||||
}
|
||||
// 病人名
|
||||
String patientName = params.getString("patientName");
|
||||
// 病人身份证
|
||||
String idNum = params.getString("idNum");
|
||||
// yyyyMMdd
|
||||
String date = params.getString("date");
|
||||
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
|
||||
vitalSignTimer.createAndSendMessageMongo(databaseName, username, simpSessionId);
|
||||
}
|
||||
|
||||
@MessageMapping("/changeAIFlag")
|
||||
public void changeAIFlag(MessageHeaders messageHeaders, String body) {
|
||||
LinkedMultiValueMap nativeHeaders = (LinkedMultiValueMap) messageHeaders.get("nativeHeaders");
|
||||
ArrayList tokenList = (ArrayList) nativeHeaders.get("token");
|
||||
String token = (String) tokenList.get(0);
|
||||
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
|
||||
|
||||
String username = authorization.getPrincipalName();
|
||||
String simpSessionId = messageHeaders.get("simpSessionId", String.class);
|
||||
|
||||
JSONObject params = JSONObject.parseObject(body);
|
||||
// 病人名
|
||||
String patientName = params.getString("patientName");
|
||||
// 病人身份证
|
||||
String idNum = params.getString("idNum");
|
||||
// yyyyMMdd
|
||||
String date = params.getString("date");
|
||||
String databaseName = DatabaseNameUtil.encrypt(patientName) + "_" + DatabaseNameUtil.encrypt(idNum) + "_" + date;
|
||||
vitalSignTimer.changeAIFlag(databaseName, username, simpSessionId, params.getString("flag"),
|
||||
params.getString("medicine"), params.getString("value"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.rax.vital.medicine.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.rax.vital.medicine.entity.AIMedicine;
|
||||
import com.rax.common.core.util.R;
|
||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
|
||||
import java.sql.Connection;
|
||||
|
@ -15,4 +14,6 @@ public interface AIMedicineService {
|
|||
List<Map> getAIMedicine(MongoTemplate template);
|
||||
|
||||
List<Map> getAIMedicine(Connection connection);
|
||||
|
||||
void changeAIFlagMedicine(MongoTemplate template, String flag, String medicine, String value);
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package com.rax.vital.medicine.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.rax.vital.medicine.entity.AIMedicine;
|
||||
import com.rax.vital.medicine.mapper.AIMedicineMapper;
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.rax.vital.medicine.service.AIMedicineService;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
@ -10,11 +8,13 @@ import org.springframework.data.domain.Sort;
|
|||
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||
import org.springframework.data.mongodb.core.query.Query;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -28,11 +28,24 @@ import java.util.Map;
|
|||
@AllArgsConstructor
|
||||
public class AIMedicineServiceImpl implements AIMedicineService {
|
||||
|
||||
private static final Map<String, String> doctorMedicineKeyMap = new HashMap() {
|
||||
{
|
||||
put("丙泊酚", "丙泊酚");
|
||||
put("舒芬太尼", "舒芬太尼");
|
||||
put("瑞芬太尼", "瑞芬太尼");
|
||||
put("顺阿曲库胺", "顺阿曲库胺");
|
||||
put("尼卡地平", "尼卡地平");
|
||||
put("艾司洛尔", "艾司洛尔");
|
||||
put("麻黄素", "麻黄素");
|
||||
put("阿托品", "阿托品");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public List<Map> getAIMedicine(MongoTemplate template) {
|
||||
Query query = new Query();
|
||||
query.limit(1);
|
||||
query.with(Sort.by(Sort.Order.desc("_id")));
|
||||
query.with(Sort.by(Sort.Order.desc("Time")));
|
||||
List<Map> aiMedicines = template.find(query, Map.class, "AIMedicineTable");
|
||||
return aiMedicines;
|
||||
}
|
||||
|
@ -40,7 +53,7 @@ public class AIMedicineServiceImpl implements AIMedicineService {
|
|||
@Override
|
||||
public List<Map> getAIMedicine(Connection connection) {
|
||||
List<Map> medicineList = new ArrayList<>();
|
||||
try {
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery("SELECT id, phase, `丙泊酚`, `舒芬太尼`, `瑞芬太尼`, `顺阿曲库胺`, `尼卡地平`, `艾司洛尔`, `麻黄素`, `阿托品`, time FROM `AIMedicineTable` ORDER BY time LIMIT 1;");
|
||||
while (resultSet.next()) {
|
||||
|
@ -59,8 +72,31 @@ public class AIMedicineServiceImpl implements AIMedicineService {
|
|||
medicineList.add(medicine);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return medicineList;
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return medicineList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changeAIFlagMedicine(MongoTemplate template, String flag, String medicine, String value) {
|
||||
BasicDBObject obj = new BasicDBObject();
|
||||
obj.put("Flag", flag);
|
||||
obj.put("Time", LocalDateTime.now());
|
||||
obj.put("ConvertFlag", "2".equals(flag) ? 1 : 0);
|
||||
template.insert(obj, "aiflagtable");
|
||||
|
||||
if (StringUtils.hasText(value) && StringUtils.hasText(medicine)) {
|
||||
BasicDBObject medicineObj = new BasicDBObject();
|
||||
for (String key : doctorMedicineKeyMap.keySet()) {
|
||||
if (key.equals(medicine)) {
|
||||
medicineObj.put(medicine, value);
|
||||
} else {
|
||||
medicineObj.put(key, 0);
|
||||
}
|
||||
}
|
||||
medicineObj.put("phase", "");
|
||||
medicineObj.put("Time", LocalDateTime.now());
|
||||
template.insert(medicineObj, "doctormedicinetable");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public class DoctorMedicineServiceImpl implements DoctorMedicineService {
|
|||
public List<Map> getDocMedicine(MongoTemplate template) {
|
||||
Query query = new Query();
|
||||
query.limit(1);
|
||||
query.with(Sort.by(Sort.Order.desc("_id")));
|
||||
query.with(Sort.by(Sort.Order.desc("Time")));
|
||||
List<Map> doctorMedicineTable = template.find(query, Map.class, "DoctorMedicineTable");
|
||||
return doctorMedicineTable;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ public class RevulsionServiceImpl implements RevulsionService {
|
|||
public List<Map> getRevulsionServiceList(MongoTemplate template) {
|
||||
Query query = new Query();
|
||||
query.limit(1);
|
||||
query.with(Sort.by(Sort.Order.desc("_id")));
|
||||
query.with(Sort.by(Sort.Order.desc("Time")));
|
||||
List<Map> revulsionTable = template.find(query, Map.class, "RevulsionTable");
|
||||
return revulsionTable;
|
||||
}
|
||||
|
|
|
@ -38,8 +38,24 @@ public class VitalSignServiceImpl implements VitalSignsService {
|
|||
public List<Map> getVitalSignsList(MongoTemplate template) {
|
||||
Query query = new Query();
|
||||
query.limit(1);
|
||||
query.with(Sort.by(Sort.Order.desc("_id")));
|
||||
query.with(Sort.by(Sort.Order.desc("Time")));
|
||||
List<Map> vitalList = template.find(query, Map.class, "featureTable");
|
||||
|
||||
if (vitalList != null && vitalList.size() > 0) {
|
||||
Map map = vitalList.get(0);
|
||||
Long bis = (Long) map.get("BIS");
|
||||
map.put("BIS_except", bis <= 40 || bis >= 60);
|
||||
Long hr = (Long) map.get("HR");
|
||||
map.put("HR_except", hr <= 50 || hr >= 80);
|
||||
Long sbp = (Long) map.get("SBP");
|
||||
map.put("SBP_except", sbp <= 90 || sbp >= 120);
|
||||
Long dbp = (Long) map.get("DBP");
|
||||
map.put("DBP_except", dbp <= 60 || dbp >= 90);
|
||||
Float st = (Float) map.get("ST");
|
||||
map.put("ST_except", st <= -0.2 || st >= 0.2);
|
||||
Long etCO2 = (Long) map.get("EtCO2");
|
||||
map.put("EtCO2_except", etCO2 <= 30 || etCO2 >= 45);
|
||||
}
|
||||
return vitalList;
|
||||
}
|
||||
|
||||
|
@ -48,7 +64,7 @@ public class VitalSignServiceImpl implements VitalSignsService {
|
|||
List<Map> vitalList = new ArrayList<>();
|
||||
try {
|
||||
Statement statement = connection.createStatement();
|
||||
ResultSet resultSet = statement.executeQuery("SELECT id, phase, bis, hr, sbp, dbp, st, temp, spo2, etco2, ppg, abg, tof, time FROM `featuretable` ORDER BY time DESC LIMIT 1;");
|
||||
ResultSet resultSet = statement.executeQuery("SELECT id, Phase, BIS, HR, SBP, DBP, ST, TEMP, SPO2, EtCO2, PPG, ABG, TOF, Time FROM `featuretable` ORDER BY time DESC LIMIT 1;");
|
||||
while (resultSet.next()) {
|
||||
Map vital = new HashMap();
|
||||
vital.put("id", resultSet.getString("id"));
|
||||
|
|
|
@ -38,16 +38,16 @@ public class VitalSignTimer {
|
|||
private final RevulsionService revulsionService;
|
||||
|
||||
// mongoDB定时任务容器
|
||||
private static final Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300);
|
||||
private static volatile Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300);
|
||||
|
||||
// mongoDB链接工具类容器
|
||||
private static final Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300);
|
||||
private static volatile Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300);
|
||||
|
||||
// mysql定时任务容器
|
||||
private static final Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300);
|
||||
private static volatile Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300);
|
||||
|
||||
// mysql链接容器
|
||||
private static final Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300);
|
||||
private static volatile Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300);
|
||||
|
||||
// MongoDB的地址
|
||||
@Value("${vital-sign.mongodb.host}")
|
||||
|
@ -73,12 +73,21 @@ public class VitalSignTimer {
|
|||
@Value("${vital-sign.mysql.password}")
|
||||
private String mysqlPassword;
|
||||
|
||||
private static volatile Map<String, String> masterControlMap = new HashMap(300);
|
||||
|
||||
/**
|
||||
* 根据当前用户和患者数据库进行查询生命体征和用药信息并推送,数据库类型是MongoDB
|
||||
*
|
||||
* @author zhaoyz
|
||||
*/
|
||||
public void createAndSendMessageMongo(String database, String username, String simpSessionId) {
|
||||
|
||||
synchronized (username) {
|
||||
if (!masterControlMap.containsKey(database)) {
|
||||
masterControlMap.put(database, username);
|
||||
}
|
||||
}
|
||||
|
||||
TimerTask task = timerMongoTaskMap.get(simpSessionId);
|
||||
if (task != null) {
|
||||
return;
|
||||
|
@ -110,7 +119,6 @@ public class VitalSignTimer {
|
|||
result.put("flags", flags);
|
||||
|
||||
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/surgeryData", result);
|
||||
|
||||
}
|
||||
};
|
||||
// 定时任务,设置1秒
|
||||
|
@ -200,4 +208,31 @@ public class VitalSignTimer {
|
|||
}
|
||||
}
|
||||
|
||||
public void changeAIFlag(String database, String username, String simpSessionId, String flag, String medicine, String value) {
|
||||
|
||||
synchronized (username) {
|
||||
if (masterControlMap.containsKey(database) && masterControlMap.get(database).equals(username)) {
|
||||
MongoDBSource mongoDBSource = mongoDBSourceMap.get(simpSessionId);
|
||||
if (mongoDBSource == null) {
|
||||
mongoDBSource = new MongoDBSource(mongoDBHost, mongoPassword, mongoUsername, database);
|
||||
mongoDBSourceMap.put(simpSessionId, mongoDBSource);
|
||||
mongoDBSource.open();
|
||||
}
|
||||
|
||||
MongoTemplate template = mongoDBSource.getTemplate();
|
||||
aiMedicineService.changeAIFlagMedicine(template, flag, medicine, value);
|
||||
|
||||
HashMap<String, Object> result = new HashMap();
|
||||
result.put("status", 0);
|
||||
result.put("flag", flag);
|
||||
result.put("msg", "");
|
||||
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/medicineData", result);
|
||||
} else {
|
||||
HashMap<String, Object> result = new HashMap();
|
||||
result.put("status", 1);
|
||||
result.put("msg", "不是主控人员");
|
||||
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/medicineData", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package com.rax.vital.util;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
public class DatabaseNameUtil {
|
||||
private static final String key = "ruianxing112232323";
|
||||
|
||||
public static String decrypt(String data) {
|
||||
Base64.Decoder decoder = Base64.getDecoder();
|
||||
return new String(xor(decoder.decode(data)));
|
||||
}
|
||||
|
||||
public static String encrypt(String data) {
|
||||
if (StringUtils.hasText(data)) {
|
||||
Base64.Encoder encoder = Base64.getEncoder();
|
||||
return encoder.encodeToString(xor(data.getBytes()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static byte[] xor(byte[] dataBytes) {
|
||||
byte[] result = new byte[dataBytes.length];
|
||||
byte[] keyBytes = DatabaseNameUtil.key.getBytes();
|
||||
int keyLen = keyBytes.length;
|
||||
|
||||
for (int i = 0; i < dataBytes.length; i++) {
|
||||
result[i] = (byte) ((dataBytes[i]) ^ (keyBytes[i % keyLen]));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user