生命体征数据

统一账户登录只能一个人
This commit is contained in:
zhaoyz 2024-04-10 09:24:16 +08:00
parent b4c11c9663
commit 8d176fc965
37 changed files with 385 additions and 307 deletions

View File

@ -1,5 +1,6 @@
package com.rax.auth.config; package com.rax.auth.config;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.CustomeOAuth2AccessTokenGenerator; import com.rax.auth.support.CustomeOAuth2AccessTokenGenerator;
import com.rax.auth.support.core.CustomeOAuth2TokenCustomizer; import com.rax.auth.support.core.CustomeOAuth2TokenCustomizer;
import com.rax.auth.support.core.RaxDaoAuthenticationProvider; 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.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; 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; import java.util.Arrays;
@Configuration @Configuration
@ -60,51 +57,52 @@ public class AuthorizationServerConfiguration {
private final RaxLoginPreFilter raxLoginPreFilter; private final RaxLoginPreFilter raxLoginPreFilter;
private final RemoteClientDetailsService clientDetailsService;
@Bean @Bean
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http, public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http,
RaxAuthenticationSuccessEventHandler successEventHandler, RaxAuthenticationSuccessEventHandler successEventHandler,
RaxAuthenticationFailureEventHandler failureEventHandler) throws Exception { RaxAuthenticationFailureEventHandler failureEventHandler) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer();
http.cors(httpSecurityCorsConfigurer -> httpSecurityCorsConfigurer.configurationSource(corsConfigurationSource()));
http.addFilterAfter(raxLoginPreFilter, UsernamePasswordAuthenticationFilter.class); http.addFilterAfter(raxLoginPreFilter, UsernamePasswordAuthenticationFilter.class);
http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点 http.with(authorizationServerConfigurer.tokenEndpoint((tokenEndpoint) -> {// 个性化认证授权端点
tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter tokenEndpoint.accessTokenRequestConverter(accessTokenRequestConverter()) // 注入自定义的授权认证Converter
.accessTokenResponseHandler(successEventHandler) // 登录成功处理器 .accessTokenResponseHandler(successEventHandler) // 登录成功处理器
.errorResponseHandler(failureEventHandler);// 登录失败处理器 .errorResponseHandler(failureEventHandler);// 登录失败处理器
}).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证 }).clientAuthentication(oAuth2ClientAuthenticationConfigurer -> // 个性化客户端认证
oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常 oAuth2ClientAuthenticationConfigurer.errorResponseHandler(failureEventHandler))// 处理客户端认证异常
.authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面 .authorizationEndpoint(authorizationEndpoint -> authorizationEndpoint// 授权码端点个性化confirm页面
.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults()) .consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI)), Customizer.withDefaults())
.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现 .with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
.authorizationServerSettings( .authorizationServerSettings(
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()), AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
Customizer.withDefaults()); Customizer.withDefaults());
AntPathRequestMatcher[] requestMatchers = permitAllUrl.getUrls() AntPathRequestMatcher[] requestMatchers = permitAllUrl.getUrls()
.stream() .stream()
.map(AntPathRequestMatcher::new) .map(AntPathRequestMatcher::new)
.toList() .toList()
.toArray(new AntPathRequestMatcher[] {}); .toArray(new AntPathRequestMatcher[]{});
http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers) http.authorizeHttpRequests(authorizeRequests -> authorizeRequests.requestMatchers(requestMatchers)
.permitAll() .permitAll()
.anyRequest() .anyRequest()
.authenticated()) .authenticated())
.oauth2ResourceServer( .oauth2ResourceServer(
oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector)) oauth2 -> oauth2.opaqueToken(token -> token.introspector(customOpaqueTokenIntrospector))
.authenticationEntryPoint(resourceAuthExceptionEntryPoint) .authenticationEntryPoint(resourceAuthExceptionEntryPoint)
.bearerTokenResolver(raxBearerTokenExtractor)) .bearerTokenResolver(raxBearerTokenExtractor))
.exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint)) .exceptionHandling(configurer -> configurer.authenticationEntryPoint(resourceAuthExceptionEntryPoint))
.headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable))
.csrf(AbstractHttpConfigurer::disable); .csrf(AbstractHttpConfigurer::disable);
http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现 http.with(authorizationServerConfigurer.authorizationService(authorizationService)// redis存储token的实现
.authorizationServerSettings( .authorizationServerSettings(
AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()), AuthorizationServerSettings.builder()/*.issuer(SecurityConstants.PROJECT_LICENSE)*/.build()),
Customizer.withDefaults()); Customizer.withDefaults());
DefaultSecurityFilterChain securityFilterChain = http.build(); DefaultSecurityFilterChain securityFilterChain = http.build();
// 注入自定义授权模式实现 // 注入自定义授权模式实现
@ -115,6 +113,7 @@ public class AuthorizationServerConfiguration {
/** /**
* 令牌生成规则实现 </br> * 令牌生成规则实现 </br>
* client:username:uuid * client:username:uuid
*
* @return OAuth2TokenGenerator * @return OAuth2TokenGenerator
*/ */
@Bean @Bean
@ -137,6 +136,7 @@ public class AuthorizationServerConfiguration {
/** /**
* request -> xToken 注入请求转换器 * request -> xToken 注入请求转换器
*
* @return DelegatingAuthenticationConverter * @return DelegatingAuthenticationConverter
*/ */
private AuthenticationConverter accessTokenRequestConverter() { private AuthenticationConverter accessTokenRequestConverter() {
@ -160,10 +160,10 @@ public class AuthorizationServerConfiguration {
OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class); OAuth2AuthorizationService authorizationService = http.getSharedObject(OAuth2AuthorizationService.class);
OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider( OAuth2ResourceOwnerPasswordAuthenticationProvider resourceOwnerPasswordAuthenticationProvider = new OAuth2ResourceOwnerPasswordAuthenticationProvider(
authenticationManager, authorizationService, oAuth2TokenGenerator()); authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider( OAuth2ResourceOwnerSmsAuthenticationProvider resourceOwnerSmsAuthenticationProvider = new OAuth2ResourceOwnerSmsAuthenticationProvider(
authenticationManager, authorizationService, oAuth2TokenGenerator()); authenticationManager, authorizationService, oAuth2TokenGenerator(), clientDetailsService);
// 处理 UsernamePasswordAuthenticationToken // 处理 UsernamePasswordAuthenticationToken
http.authenticationProvider(new RaxDaoAuthenticationProvider()); http.authenticationProvider(new RaxDaoAuthenticationProvider());
@ -173,17 +173,4 @@ public class AuthorizationServerConfiguration {
http.authenticationProvider(resourceOwnerSmsAuthenticationProvider); 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;
}
} }

View File

@ -1,5 +1,6 @@
package com.rax.auth.endpoint; package com.rax.auth.endpoint;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
@ -170,6 +171,9 @@ public class RaxTokenEndpoint {
cacheManager.getCache(CacheConstants.USER_DETAILS).evictIfPresent(authorization.getPrincipalName()); cacheManager.getCache(CacheConstants.USER_DETAILS).evictIfPresent(authorization.getPrincipalName());
// 清空access token // 清空access token
authorizationService.remove(authorization); 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( SpringContextHolder.publishEvent(new LogoutSuccessEvent(new PreAuthenticatedAuthenticationToken(
authorization.getPrincipalName(), authorization.getRegisteredClientId()))); authorization.getPrincipalName(), authorization.getRegisteredClientId())));

View File

@ -1,11 +1,22 @@
package com.rax.auth.support.base; package com.rax.auth.support.base;
import cn.hutool.core.codec.Base64;
import cn.hutool.extra.spring.SpringUtil; 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.OAuth2ErrorCodesExpand;
import com.rax.common.security.util.ScopeException; import com.rax.common.security.util.ScopeException;
import jakarta.annotation.Resource;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.MessageSourceAccessor; import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.*; import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException; 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.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.security.Principal; import java.security.Principal;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier; import java.util.function.Supplier;
/** /**
* @author jumuning * @author jumuning
* @description * @description 处理自定义授权
*
* 处理自定义授权
*/ */
public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OAuth2ResourceOwnerBaseAuthenticationToken> public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OAuth2ResourceOwnerBaseAuthenticationToken>
implements AuthenticationProvider { implements AuthenticationProvider {
@ -53,21 +64,29 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
@Deprecated @Deprecated
private Supplier<String> refreshTokenGenerator; private Supplier<String> refreshTokenGenerator;
private static final String LOGGED_IN = "rax_logged_in::";
private final RemoteClientDetailsService clientDetailsService;
/** /**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters. * provided parameters.
*
* @param authorizationService the authorization service * @param authorizationService the authorization service
* @param tokenGenerator the token generator * @param tokenGenerator the token generator
* @since 0.2.3 * @since 0.2.3
*/ */
public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager, public OAuth2ResourceOwnerBaseAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService, OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) { OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
RemoteClientDetailsService clientDetailsService
) {
Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(authorizationService, "authorizationService cannot be null");
Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); Assert.notNull(tokenGenerator, "tokenGenerator cannot be null");
this.authenticationManager = authenticationManager; this.authenticationManager = authenticationManager;
this.authorizationService = authorizationService; this.authorizationService = authorizationService;
this.tokenGenerator = tokenGenerator; this.tokenGenerator = tokenGenerator;
this.clientDetailsService = clientDetailsService;
// 国际化配置 // 国际化配置
this.messages = new MessageSourceAccessor(SpringUtil.getBean("securityMessageSource"), Locale.CHINA); this.messages = new MessageSourceAccessor(SpringUtil.getBean("securityMessageSource"), Locale.CHINA);
@ -83,6 +102,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/** /**
* 当前provider是否支持此令牌类型 * 当前provider是否支持此令牌类型
*
* @param authentication * @param authentication
* @return * @return
*/ */
@ -91,6 +111,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/** /**
* 当前的请求客户端是否支持此模式 * 当前的请求客户端是否支持此模式
*
* @param registeredClient * @param registeredClient
*/ */
public abstract void checkClient(RegisteredClient 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 * Performs authentication with the same contract as
* {@link AuthenticationManager#authenticate(Authentication)} . * {@link AuthenticationManager#authenticate(Authentication)} .
*
* @param authentication the authentication request object. * @param authentication the authentication request object.
* @return a fully authenticated object including credentials. May return * @return a fully authenticated object including credentials. May return
* <code>null</code> if the <code>AuthenticationProvider</code> is unable to support * <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()); authorizedScopes = new LinkedHashSet<>(resouceOwnerBaseAuthentication.getScopes());
} } else {
else {
authorizedScopes = new LinkedHashSet<>(); authorizedScopes = new LinkedHashSet<>();
} }
@ -139,7 +160,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken); LOGGER.debug("got usernamePasswordAuthenticationToken=" + usernamePasswordAuthenticationToken);
Authentication usernamePasswordAuthentication = authenticationManager Authentication usernamePasswordAuthentication = authenticationManager
.authenticate(usernamePasswordAuthenticationToken); .authenticate(usernamePasswordAuthenticationToken);
// @formatter:off // @formatter:off
DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder() DefaultOAuth2TokenContext.Builder tokenContextBuilder = DefaultOAuth2TokenContext.builder()
@ -152,11 +173,11 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
// @formatter:on // @formatter:on
OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization
.withRegisteredClient(registeredClient) .withRegisteredClient(registeredClient)
.principalName(usernamePasswordAuthentication.getName()) .principalName(usernamePasswordAuthentication.getName())
.authorizationGrantType(AuthorizationGrantType.PASSWORD) .authorizationGrantType(AuthorizationGrantType.PASSWORD)
// 0.4.0 新增的方法 // 0.4.0 新增的方法
.authorizedScopes(authorizedScopes); .authorizedScopes(authorizedScopes);
// ----- Access token ----- // ----- Access token -----
OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build(); OAuth2TokenContext tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.ACCESS_TOKEN).build();
@ -171,29 +192,27 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());
if (generatedAccessToken instanceof ClaimAccessor) { if (generatedAccessToken instanceof ClaimAccessor) {
authorizationBuilder.id(accessToken.getTokenValue()) authorizationBuilder.id(accessToken.getTokenValue())
.token(accessToken, .token(accessToken,
(metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME,
((ClaimAccessor) generatedAccessToken).getClaims())) ((ClaimAccessor) generatedAccessToken).getClaims()))
// 0.4.0 新增的方法 // 0.4.0 新增的方法
.authorizedScopes(authorizedScopes) .authorizedScopes(authorizedScopes)
.attribute(Principal.class.getName(), usernamePasswordAuthentication); .attribute(Principal.class.getName(), usernamePasswordAuthentication);
} } else {
else {
authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken); authorizationBuilder.id(accessToken.getTokenValue()).accessToken(accessToken);
} }
// ----- Refresh token ----- // ----- Refresh token -----
OAuth2RefreshToken refreshToken = null; OAuth2RefreshToken refreshToken = null;
if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN) && 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)) { !clientPrincipal.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.NONE)) {
if (this.refreshTokenGenerator != null) { if (this.refreshTokenGenerator != null) {
Instant issuedAt = Instant.now(); Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive()); Instant expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getRefreshTokenTimeToLive());
refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt); refreshToken = new OAuth2RefreshToken(this.refreshTokenGenerator.get(), issuedAt, expiresAt);
} } else {
else {
tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build(); tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build();
OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext); OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext);
if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) { if (!(generatedRefreshToken instanceof OAuth2RefreshToken)) {
@ -208,6 +227,8 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
OAuth2Authorization authorization = authorizationBuilder.build(); OAuth2Authorization authorization = authorizationBuilder.build();
checkIsLogin(authorization);
this.authorizationService.save(authorization); this.authorizationService.save(authorization);
LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken"); LOGGER.debug("returning OAuth2AccessTokenAuthenticationToken");
@ -215,8 +236,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken,
refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims())); refreshToken, Objects.requireNonNull(authorization.getAccessToken().getClaims()));
} } catch (Exception ex) {
catch (Exception ex) {
LOGGER.error("problem in authenticate", ex); LOGGER.error("problem in authenticate", ex);
throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex); throw oAuth2AuthenticationException(authentication, (AuthenticationException) ex);
} }
@ -225,15 +245,16 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
/** /**
* 登录异常转换为oauth2异常 * 登录异常转换为oauth2异常
* @param authentication 身份验证 *
* @param authentication 身份验证
* @param authenticationException 身份验证异常 * @param authenticationException 身份验证异常
* @return {@link OAuth2AuthenticationException} * @return {@link OAuth2AuthenticationException}
*/ */
private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication, private OAuth2AuthenticationException oAuth2AuthenticationException(Authentication authentication,
AuthenticationException authenticationException) { AuthenticationException authenticationException) {
if (authenticationException instanceof UsernameNotFoundException) { if (authenticationException instanceof UsernameNotFoundException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USERNAME_NOT_FOUND, 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"), "Username {0} not found"),
"")); ""));
} }
@ -244,7 +265,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
} }
if (authenticationException instanceof LockedException) { if (authenticationException instanceof LockedException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_LOCKED, this.messages 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) { if (authenticationException instanceof DisabledException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE, return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_DISABLE,
@ -253,7 +274,7 @@ public abstract class OAuth2ResourceOwnerBaseAuthenticationProvider<T extends OA
} }
if (authenticationException instanceof AccountExpiredException) { if (authenticationException instanceof AccountExpiredException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.USER_EXPIRED, this.messages 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) { if (authenticationException instanceof CredentialsExpiredException) {
return new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodesExpand.CREDENTIALS_EXPIRED, 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); 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);
}
} }

View File

@ -1,5 +1,6 @@
package com.rax.auth.support.password; package com.rax.auth.support.password;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider; import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -28,15 +29,17 @@ public class OAuth2ResourceOwnerPasswordAuthenticationProvider
/** /**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters. * provided parameters.
*
* @param authenticationManager * @param authenticationManager
* @param authorizationService the authorization service * @param authorizationService the authorization service
* @param tokenGenerator the token generator * @param tokenGenerator the token generator
* @since 0.2.3 * @since 0.2.3
*/ */
public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager, public OAuth2ResourceOwnerPasswordAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService, OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) { OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
super(authenticationManager, authorizationService, tokenGenerator); RemoteClientDetailsService clientDetailsService) {
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
} }
@Override @Override

View File

@ -1,5 +1,6 @@
package com.rax.auth.support.sms; package com.rax.auth.support.sms;
import com.rax.admin.api.feign.RemoteClientDetailsService;
import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider; import com.rax.auth.support.base.OAuth2ResourceOwnerBaseAuthenticationProvider;
import com.rax.common.core.constant.SecurityConstants; import com.rax.common.core.constant.SecurityConstants;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -19,7 +20,7 @@ import java.util.Map;
/** /**
* @author lengleng * @author lengleng
* @date date * @date date
* * <p>
* 短信登录的核心处理 * 短信登录的核心处理
*/ */
public class OAuth2ResourceOwnerSmsAuthenticationProvider public class OAuth2ResourceOwnerSmsAuthenticationProvider
@ -30,15 +31,17 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
/** /**
* Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the
* provided parameters. * provided parameters.
*
* @param authenticationManager * @param authenticationManager
* @param authorizationService the authorization service * @param authorizationService the authorization service
* @param tokenGenerator the token generator * @param tokenGenerator the token generator
* @since 0.2.3 * @since 0.2.3
*/ */
public OAuth2ResourceOwnerSmsAuthenticationProvider(AuthenticationManager authenticationManager, public OAuth2ResourceOwnerSmsAuthenticationProvider(AuthenticationManager authenticationManager,
OAuth2AuthorizationService authorizationService, OAuth2AuthorizationService authorizationService,
OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator) { OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator,
super(authenticationManager, authorizationService, tokenGenerator); RemoteClientDetailsService clientDetailsService) {
super(authenticationManager, authorizationService, tokenGenerator, clientDetailsService);
} }
@Override @Override
@ -52,7 +55,7 @@ public class OAuth2ResourceOwnerSmsAuthenticationProvider
public void checkClient(RegisteredClient registeredClient) { public void checkClient(RegisteredClient registeredClient) {
assert registeredClient != null; assert registeredClient != null;
if (!registeredClient.getAuthorizationGrantTypes() if (!registeredClient.getAuthorizationGrantTypes()
.contains(new AuthorizationGrantType(SecurityConstants.MOBILE))) { .contains(new AuthorizationGrantType(SecurityConstants.MOBILE))) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
} }
} }

View File

@ -33,7 +33,6 @@ import java.util.*;
import java.util.regex.Pattern; import java.util.regex.Pattern;
/** /**
* @author lengleng
* @date 2020-03-11 * @date 2020-03-11
* <p> * <p>
* 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理 * 资源服务器对外直接暴露URL,如果设置contex-path 要特殊处理

View File

@ -59,6 +59,11 @@ public class OpenAPIDefinition extends OpenAPI implements InitializingBean, Appl
private ApplicationContext applicationContext; private ApplicationContext applicationContext;
/**
*
* @param swaggerProperties
* @return
*/
private SecurityScheme securityScheme(SwaggerProperties swaggerProperties) { private SecurityScheme securityScheme(SwaggerProperties swaggerProperties) {
OAuthFlow clientCredential = new OAuthFlow(); OAuthFlow clientCredential = new OAuthFlow();
clientCredential.setTokenUrl(swaggerProperties.getTokenUrl()); clientCredential.setTokenUrl(swaggerProperties.getTokenUrl());

View File

@ -17,11 +17,11 @@
<modules> <modules>
<module>common-bom</module> <module>common-bom</module>
<module>common-core</module> <module>common-core</module>
<module>common-datasource</module> <!--<module>common-datasource</module>-->
<module>common-log</module> <module>common-log</module>
<module>common-mybatis</module> <module>common-mybatis</module>
<module>common-oss</module> <module>common-oss</module>
<module>common-seata</module> <!--<module>common-seata</module>-->
<module>common-security</module> <module>common-security</module>
<module>common-feign</module> <module>common-feign</module>
<module>common-swagger</module> <module>common-swagger</module>

View File

@ -623,6 +623,8 @@ CREATE TABLE `sys_user` (
`wx_openid` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '微信登录openId', `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', `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', `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, PRIMARY KEY (`user_id`) USING BTREE,
KEY `user_wx_openid` (`wx_openid`) USING BTREE, KEY `user_wx_openid` (`wx_openid`) USING BTREE,
KEY `user_qq_openid` (`qq_openid`) USING BTREE, KEY `user_qq_openid` (`qq_openid`) USING BTREE,

View File

@ -89,7 +89,7 @@
<modules> <modules>
<!-- 代码生成模块 --> <!-- 代码生成模块 -->
<module>codegen</module> <!--<module>codegen</module>-->
<!-- Quartz模块 --> <!-- Quartz模块 -->
<module>quartz</module> <module>quartz</module>
<!-- 身份验证模块 --> <!-- 身份验证模块 -->

View File

@ -36,7 +36,7 @@ public class SysLog implements Serializable {
* 日志类型 * 日志类型
*/ */
@NotBlank(message = "日志类型不能为空") @NotBlank(message = "日志类型不能为空")
@ExcelProperty("日志类型0-正常 9-错误") @ExcelProperty("日志类型0-正常 9-错误 1-添加 2-删除 3-编辑")
@Schema(description = "日志类型") @Schema(description = "日志类型")
private String logType; private String logType;

View File

@ -148,6 +148,6 @@ public class SysUser implements Serializable {
private String sex; private String sex;
@Schema(description = "医院") @Schema(description = "医院")
private String hospital; private String hospitalId;
} }

View File

@ -49,7 +49,7 @@ public class SysHospitalController {
return sysHospitalService.getHospitalList(name, offset, limit); return sysHospitalService.getHospitalList(name, offset, limit);
} }
@RequestMapping("/getHospitalList") @PostMapping("/getHospitalList")
R<List> getHospitalList(String name) { R<List> getHospitalList(String name) {
return sysHospitalService.getHospitalList(name); return sysHospitalService.getHospitalList(name);
} }

View File

@ -39,7 +39,8 @@ public class SysLogController {
/** /**
* 简单分页查询 * 简单分页查询
* @param page 分页对象 *
* @param page 分页对象
* @param sysLog 系统日志 * @param sysLog 系统日志
* @return * @return
*/ */
@ -50,6 +51,7 @@ public class SysLogController {
/** /**
* 批量删除日志 * 批量删除日志
*
* @param ids ID * @param ids ID
* @return success/false * @return success/false
*/ */
@ -61,6 +63,7 @@ public class SysLogController {
/** /**
* 插入日志 * 插入日志
*
* @param sysLog 日志实体 * @param sysLog 日志实体
* @return success/false * @return success/false
*/ */
@ -72,6 +75,7 @@ public class SysLogController {
/** /**
* 导出excel 表格 * 导出excel 表格
*
* @param sysLog 查询条件 * @param sysLog 查询条件
* @return * @return
*/ */
@ -82,4 +86,9 @@ public class SysLogController {
return sysLogService.list(Wrappers.query(sysLog)); return sysLogService.list(Wrappers.query(sysLog));
} }
@PostMapping("/getMonthlyLogCount")
public R getMonthlyLogCount(String startTime, String endTime) {
return R.ok(sysLogService.getMonthlyLogCount(startTime, endTime));
}
} }

View File

@ -3,16 +3,17 @@ package com.rax.admin.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rax.admin.api.entity.SysLog; import com.rax.admin.api.entity.SysLog;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
* 日志表 Mapper 接口 * 日志表 Mapper 接口
* </p> * </p>
*
* @author lengleng
* @since 2017-11-20
*/ */
@Mapper @Mapper
public interface SysLogMapper extends BaseMapper<SysLog> { public interface SysLogMapper extends BaseMapper<SysLog> {
List<Map> getMonthlyLogCount(@Param("startTime") String startTime, @Param("endTime") String endTime);
} }

View File

@ -5,6 +5,9 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.rax.admin.api.dto.SysLogDTO; import com.rax.admin.api.dto.SysLogDTO;
import com.rax.admin.api.entity.SysLog; import com.rax.admin.api.entity.SysLog;
import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
* 日志表 服务类 * 日志表 服务类
@ -30,4 +33,6 @@ public interface SysLogService extends IService<SysLog> {
*/ */
Boolean saveLog(SysLog sysLog); Boolean saveLog(SysLog sysLog);
List<Map> getMonthlyLogCount(String startTime, String endTime);
} }

View File

@ -13,6 +13,9 @@ import com.rax.admin.service.SysLogService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Map;
/** /**
* <p> * <p>
* 日志表 服务实现类 * 日志表 服务实现类
@ -52,4 +55,8 @@ public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> impleme
return Boolean.TRUE; return Boolean.TRUE;
} }
@Override
public List<Map> getMonthlyLogCount(String startTime, String endTime) {
return baseMapper.getMonthlyLogCount(startTime, endTime);
}
} }

View File

@ -1,5 +1,5 @@
server: server:
port: 9999 port: 8888
servlet: servlet:
context-path: /admin context-path: /admin
@ -17,7 +17,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
username: root username: root
password: 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: quartz:
properties: properties:
@ -46,6 +46,7 @@ spring:
multipart: multipart:
max-file-size: 100MB max-file-size: 100MB
max-request-size: 100MB max-request-size: 100MB
# 静态资源文件
mvc: mvc:
static-path-pattern: /static/** static-path-pattern: /static/**
@ -79,10 +80,6 @@ security:
- /rax/** - /rax/**
- /hospital/getHospitalList - /hospital/getHospitalList
# 临时白名单 # 临时白名单
- /static/**
# - /topic/**
# - /front/**
# - /medicine/**
#--------------如下配置尽量不要变动------------- #--------------如下配置尽量不要变动-------------

View File

@ -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>

View File

@ -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());
});

View File

@ -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>

View File

@ -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

View File

@ -44,17 +44,15 @@ public class WSChannelInterceptor implements ChannelInterceptor {
String token = accessToken.get(0); String token = accessToken.get(0);
OAuth2Authorization authorization = authorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN); 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) { if (authorization == null) {
throw new AccessDeniedException("Access is denied"); throw new AccessDeniedException("Access is denied");
} }
} }
} }
if (StompCommand.ABORT.equals(accessor.getCommand())) { if (StompCommand.DISCONNECT.equals(accessor.getCommand())
System.out.println("StompCommand.ABORT"); || StompCommand.UNSUBSCRIBE.equals(accessor.getCommand()) || StompCommand.ABORT.equals(accessor.getCommand())) {
} else if (StompCommand.DISCONNECT.equals(accessor.getCommand())
|| StompCommand.UNSUBSCRIBE.equals(accessor.getCommand())) {
String simpSessionId = (String) accessor.getHeader("simpSessionId"); String simpSessionId = (String) accessor.getHeader("simpSessionId");
vitalSignTimer.stopTimerTaskMongo(simpSessionId); vitalSignTimer.stopTimerTaskMongo(simpSessionId);
chatService.stopTimerTaskMongo(simpSessionId); chatService.stopTimerTaskMongo(simpSessionId);

View File

@ -2,6 +2,7 @@ package com.rax.vital.medicine.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.rax.vital.medicine.service.ChatService; import com.rax.vital.medicine.service.ChatService;
import com.rax.vital.util.DatabaseNameUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.MessageMapping; 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.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import java.util.HashMap;
@Controller @Controller
public class ChatController { public class ChatController {
@ -27,9 +30,16 @@ public class ChatController {
if (authorization != null) { if (authorization != null) {
String username = authorization.getPrincipalName(); String username = authorization.getPrincipalName();
String simpSessionId = messageHeaders.get("simpSessionId", String.class); 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"); String msg = params.getString("msg");
chatService.sendMessage(db, username, simpSessionId, msg); chatService.sendMessage(databaseName, username, simpSessionId, msg);
} else { } else {
throw new AccessDeniedException("Access is denied"); throw new AccessDeniedException("Access is denied");
} }

View File

@ -2,17 +2,21 @@ package com.rax.vital.medicine.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.rax.vital.timer.VitalSignTimer; 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.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.handler.annotation.MessageMapping; 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.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType; import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.stereotype.Controller; 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") @MessageMapping("/getSurgeryData")
public void doctorMedicine(MessageHeaders messageHeaders, String body) { 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); JSONObject params = JSONObject.parseObject(body);
OAuth2Authorization authorization = authorizationService.findByToken(params.getString("token"), OAuth2TokenType.ACCESS_TOKEN); // 病人名
if (authorization != null) { String patientName = params.getString("patientName");
String username = authorization.getPrincipalName(); // 病人身份证
String simpSessionId = messageHeaders.get("simpSessionId", String.class); String idNum = params.getString("idNum");
vitalSignTimer.createAndSendMessageMongo(params.getString("db"), username, simpSessionId); // yyyyMMdd
} else { String date = params.getString("date");
throw new AccessDeniedException("Access is denied"); 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"));
} }
} }

View File

@ -1,7 +1,6 @@
package com.rax.vital.medicine.service; package com.rax.vital.medicine.service;
import com.baomidou.mybatisplus.extension.service.IService; import com.rax.common.core.util.R;
import com.rax.vital.medicine.entity.AIMedicine;
import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.MongoTemplate;
import java.sql.Connection; import java.sql.Connection;
@ -15,4 +14,6 @@ public interface AIMedicineService {
List<Map> getAIMedicine(MongoTemplate template); List<Map> getAIMedicine(MongoTemplate template);
List<Map> getAIMedicine(Connection connection); List<Map> getAIMedicine(Connection connection);
void changeAIFlagMedicine(MongoTemplate template, String flag, String medicine, String value);
} }

View File

@ -1,8 +1,6 @@
package com.rax.vital.medicine.service.impl; package com.rax.vital.medicine.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.mongodb.BasicDBObject;
import com.rax.vital.medicine.entity.AIMedicine;
import com.rax.vital.medicine.mapper.AIMedicineMapper;
import com.rax.vital.medicine.service.AIMedicineService; import com.rax.vital.medicine.service.AIMedicineService;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; 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.MongoTemplate;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.sql.Connection; import java.sql.Connection;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -28,11 +28,24 @@ import java.util.Map;
@AllArgsConstructor @AllArgsConstructor
public class AIMedicineServiceImpl implements AIMedicineService { public class AIMedicineServiceImpl implements AIMedicineService {
private static final Map<String, String> doctorMedicineKeyMap = new HashMap() {
{
put("丙泊酚", "丙泊酚");
put("舒芬太尼", "舒芬太尼");
put("瑞芬太尼", "瑞芬太尼");
put("顺阿曲库胺", "顺阿曲库胺");
put("尼卡地平", "尼卡地平");
put("艾司洛尔", "艾司洛尔");
put("麻黄素", "麻黄素");
put("阿托品", "阿托品");
}
};
@Override @Override
public List<Map> getAIMedicine(MongoTemplate template) { public List<Map> getAIMedicine(MongoTemplate template) {
Query query = new Query(); Query query = new Query();
query.limit(1); 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"); List<Map> aiMedicines = template.find(query, Map.class, "AIMedicineTable");
return aiMedicines; return aiMedicines;
} }
@ -40,7 +53,7 @@ public class AIMedicineServiceImpl implements AIMedicineService {
@Override @Override
public List<Map> getAIMedicine(Connection connection) { public List<Map> getAIMedicine(Connection connection) {
List<Map> medicineList = new ArrayList<>(); List<Map> medicineList = new ArrayList<>();
try { try {
Statement statement = connection.createStatement(); Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery("SELECT id, phase, `丙泊酚`, `舒芬太尼`, `瑞芬太尼`, `顺阿曲库胺`, `尼卡地平`, `艾司洛尔`, `麻黄素`, `阿托品`, time FROM `AIMedicineTable` ORDER BY time LIMIT 1;"); ResultSet resultSet = statement.executeQuery("SELECT id, phase, `丙泊酚`, `舒芬太尼`, `瑞芬太尼`, `顺阿曲库胺`, `尼卡地平`, `艾司洛尔`, `麻黄素`, `阿托品`, time FROM `AIMedicineTable` ORDER BY time LIMIT 1;");
while (resultSet.next()) { while (resultSet.next()) {
@ -59,8 +72,31 @@ public class AIMedicineServiceImpl implements AIMedicineService {
medicineList.add(medicine); medicineList.add(medicine);
} }
} catch (SQLException e) { } catch (SQLException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
return medicineList; 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");
}
} }
} }

View File

@ -32,7 +32,7 @@ public class DoctorMedicineServiceImpl implements DoctorMedicineService {
public List<Map> getDocMedicine(MongoTemplate template) { public List<Map> getDocMedicine(MongoTemplate template) {
Query query = new Query(); Query query = new Query();
query.limit(1); 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"); List<Map> doctorMedicineTable = template.find(query, Map.class, "DoctorMedicineTable");
return doctorMedicineTable; return doctorMedicineTable;
} }

View File

@ -31,7 +31,7 @@ public class RevulsionServiceImpl implements RevulsionService {
public List<Map> getRevulsionServiceList(MongoTemplate template) { public List<Map> getRevulsionServiceList(MongoTemplate template) {
Query query = new Query(); Query query = new Query();
query.limit(1); 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"); List<Map> revulsionTable = template.find(query, Map.class, "RevulsionTable");
return revulsionTable; return revulsionTable;
} }

View File

@ -38,8 +38,24 @@ public class VitalSignServiceImpl implements VitalSignsService {
public List<Map> getVitalSignsList(MongoTemplate template) { public List<Map> getVitalSignsList(MongoTemplate template) {
Query query = new Query(); Query query = new Query();
query.limit(1); 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"); 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; return vitalList;
} }
@ -48,7 +64,7 @@ public class VitalSignServiceImpl implements VitalSignsService {
List<Map> vitalList = new ArrayList<>(); List<Map> vitalList = new ArrayList<>();
try { try {
Statement statement = connection.createStatement(); 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()) { while (resultSet.next()) {
Map vital = new HashMap(); Map vital = new HashMap();
vital.put("id", resultSet.getString("id")); vital.put("id", resultSet.getString("id"));

View File

@ -38,16 +38,16 @@ public class VitalSignTimer {
private final RevulsionService revulsionService; private final RevulsionService revulsionService;
// mongoDB定时任务容器 // mongoDB定时任务容器
private static final Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300); private static volatile Map<String, TimerTask> timerMongoTaskMap = new HashMap<>(300);
// mongoDB链接工具类容器 // mongoDB链接工具类容器
private static final Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300); private static volatile Map<String, MongoDBSource> mongoDBSourceMap = new HashMap<>(300);
// mysql定时任务容器 // mysql定时任务容器
private static final Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300); private static volatile Map<String, TimerTask> timerMysqlTaskMap = new HashMap<>(300);
// mysql链接容器 // mysql链接容器
private static final Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300); private static volatile Map<String, MySQLSource> mysqlConnectionMap = new HashMap(300);
// MongoDB的地址 // MongoDB的地址
@Value("${vital-sign.mongodb.host}") @Value("${vital-sign.mongodb.host}")
@ -73,12 +73,21 @@ public class VitalSignTimer {
@Value("${vital-sign.mysql.password}") @Value("${vital-sign.mysql.password}")
private String mysqlPassword; private String mysqlPassword;
private static volatile Map<String, String> masterControlMap = new HashMap(300);
/** /**
* 根据当前用户和患者数据库进行查询生命体征和用药信息并推送数据库类型是MongoDB * 根据当前用户和患者数据库进行查询生命体征和用药信息并推送数据库类型是MongoDB
* *
* @author zhaoyz * @author zhaoyz
*/ */
public void createAndSendMessageMongo(String database, String username, String simpSessionId) { public void createAndSendMessageMongo(String database, String username, String simpSessionId) {
synchronized (username) {
if (!masterControlMap.containsKey(database)) {
masterControlMap.put(database, username);
}
}
TimerTask task = timerMongoTaskMap.get(simpSessionId); TimerTask task = timerMongoTaskMap.get(simpSessionId);
if (task != null) { if (task != null) {
return; return;
@ -110,7 +119,6 @@ public class VitalSignTimer {
result.put("flags", flags); result.put("flags", flags);
simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/surgeryData", result); simpMessagingTemplate.convertAndSendToUser(username + ":" + database, "/surgeryData", result);
} }
}; };
// 定时任务设置1秒 // 定时任务设置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);
}
}
}
} }

View File

@ -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;
}
}