Commit b822b4d2 by tangyi

Merge branch 'dev'

parents 8660b235 ec73b1e7
......@@ -46,7 +46,7 @@
| 名称 | 版本 |
| --------- | -------- |
| `Spring Boot` | `2.1.8.RELEASE` |
| `Spring Boot` | `2.1.9.RELEASE` |
| `Spring Cloud` | `Greenwich.SR3` |
# 5 系统架构
......
......@@ -35,5 +35,11 @@
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
<!-- jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
</dependencies>
</project>
package com.github.tangyi.common.security.annotations;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.*;
/**
* 超级管理员权限注解
*
* @author tangyi
* @date 2019/11/02 12:33
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PreAuthorize("hasRole(T(com.github.tangyi.common.security.enums.Roles).ROLE_ADMIN)")
public @interface AdminAuthorization {
}
package com.github.tangyi.common.security.annotations;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.*;
/**
* 租户或超管权限
*
* @author tangyi
* @date 2019/11/02 12:40
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PreAuthorize("hasRole(T(com.github.tangyi.common.security.enums.Roles).ROLE_ADMIN) or hasRole(T(com.github.tangyi.common.security.enums.Roles).ROLE_TENANT_ADMIN) or hasRole(T(com.github.tangyi.common.security.enums.Roles).ROLE_TEACHER)")
public @interface AdminTenantTeacherAuthorization {
}
package com.github.tangyi.common.security.annotations;
import org.springframework.security.access.prepost.PreAuthorize;
import java.lang.annotation.*;
/**
* 普通用户权限
*
* @author tangyi
* @date 2019/11/02 12:44
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PreAuthorize("hasRole(T(com.github.tangyi.common.security.enums.Roles).ROLE_USER)")
public @interface UserAuthorization {
}
......@@ -7,16 +7,6 @@ package com.github.tangyi.common.security.constant;
public class SecurityConstant {
/**
* 基础角色
*/
public static final String BASE_ROLE = "role_user";
/**
* 超级管理员角色
*/
public static final String ROLE_ADMIN = "role_admin";
/**
* 租户管理员角色
*/
public static final String ROLE_TENANT_ADMIN = "role_tenant_admin";
......@@ -37,11 +27,6 @@ public class SecurityConstant {
public static final String NORMAL = "0";
/**
* 异常状态
*/
public static final String ABNORMAL = "1";
/**
* 手机登录URL
*/
public static final String MOBILE_TOKEN_URL = "/mobile/token";
......
......@@ -16,35 +16,35 @@ public interface CustomUserDetailsService {
/**
* 根据用户名和租户标识查询
*
* @param username username
* @param tenantCode tenantCode
* @param username username
* @return UserDetails
* @author tangyi
* @date 2019/05/28 21:06
*/
UserDetails loadUserByIdentifierAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException;
UserDetails loadUserByIdentifierAndTenantCode(String tenantCode, String username) throws UsernameNotFoundException;
/**
* 根据社交账号和租户标识查询
*
* @param social social
* @param tenantCode tenantCode
* @param social social
* @param mobileUser mobileUser
* @return UserDetails
* @author tangyi
* @date 2019/06/22 21:08
*/
UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode, MobileUser mobileUser) throws UsernameNotFoundException;
UserDetails loadUserBySocialAndTenantCode(String tenantCode, String social, MobileUser mobileUser) throws UsernameNotFoundException;
/**
* 根据微信openId和租户标识查询
*
* @param code code
* @param tenantCode tenantCode
* @param code code
* @param wxUser wxUser
* @return UserDetails
* @author tangyi
* @date 2019/07/05 20:04:59
*/
UserDetails loadUserByWxCodeAndTenantCode(String code, String tenantCode, WxUser wxUser) throws UsernameNotFoundException;
UserDetails loadUserByWxCodeAndTenantCode(String tenantCode, String code, WxUser wxUser) throws UsernameNotFoundException;
}
package com.github.tangyi.common.security.enums;
import org.springframework.security.core.GrantedAuthority;
/**
* 角色枚举
*
* @author tangyi
* @date 2019/11/02 12:30
*/
public enum Roles implements GrantedAuthority {
/**
* 普通用户
*/
ROLE_USER,
/**
* 超级管理员
*/
ROLE_ADMIN,
/**
* 租户管理员
*/
ROLE_TENANT_ADMIN,
/**
* 老师
*/
ROLE_TEACHER;
@Override
public String getAuthority() {
return name();
}
}
package com.github.tangyi.common.security.event;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
/**
*
* 登录失败事件
*
* @author tangyi
* @date 2019-11-11 23:46
*/
@Data
public class CustomAuthenticationFailureEvent extends ApplicationEvent {
private UserDetails userDetails;
public CustomAuthenticationFailureEvent(Authentication authentication, UserDetails userDetails) {
super(authentication);
this.userDetails = userDetails;
}
}
package com.github.tangyi.common.security.event;
import lombok.Data;
import org.springframework.context.ApplicationEvent;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
/**
* 登录成功事件
*
* @author tangyi
* @date 2019-11-11 23:40
*/
@Data
public class CustomAuthenticationSuccessEvent extends ApplicationEvent {
private UserDetails userDetails;
public CustomAuthenticationSuccessEvent(Authentication authentication, UserDetails userDetails) {
super(authentication);
this.userDetails = userDetails;
}
}
package com.github.tangyi.common.security.exceptions;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.github.tangyi.common.security.serializer.CustomOauthExceptionSerializer;
import lombok.Data;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
/**
* 定制OAuth2Exception
*
* @author tangyi
* @date 2019/3/18 22:36
*/
@Data
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
/**
* 错误码
*/
private int code;
public CustomOauthException(String msg, int code) {
super(msg);
this.code = code;
}
}
package com.github.tangyi.common.security.mobile;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
......@@ -24,17 +27,21 @@ public class MobileAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService customUserDetailsService;
private ApplicationEventPublisher publisher;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MobileAuthenticationToken mobileAuthenticationToken = (MobileAuthenticationToken) authentication;
String principal = mobileAuthenticationToken.getPrincipal().toString();
UserDetails userDetails = customUserDetailsService.loadUserBySocialAndTenantCode(principal, TenantContextHolder.getTenantCode(), mobileAuthenticationToken.getMobileUser());
UserDetails userDetails = customUserDetailsService.loadUserBySocialAndTenantCode(TenantContextHolder.getTenantCode(), principal, mobileAuthenticationToken.getMobileUser());
if (userDetails == null) {
log.debug("Authentication failed: no credentials provided");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
}
MobileAuthenticationToken authenticationToken = new MobileAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationToken.setDetails(mobileAuthenticationToken.getDetails());
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
return authenticationToken;
}
......
......@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
......@@ -27,6 +28,9 @@ public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultS
@Autowired
private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private AuthenticationSuccessHandler mobileLoginSuccessHandler;
private CustomUserDetailsService userDetailsService;
......@@ -40,6 +44,7 @@ public class MobileSecurityConfigurer extends SecurityConfigurerAdapter<DefaultS
mobileAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
MobileAuthenticationProvider mobileAuthenticationProvider = new MobileAuthenticationProvider();
mobileAuthenticationProvider.setCustomUserDetailsService(userDetailsService);
mobileAuthenticationProvider.setPublisher(applicationEventPublisher);
// 增加手机登录的过滤器
http.authenticationProvider(mobileAuthenticationProvider).addFilterAfter(mobileAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
......
package com.github.tangyi.common.security.serializer;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import com.github.tangyi.common.security.exceptions.CustomOauthException;
import java.io.IOException;
import java.util.Map;
/**
* 定制OauthException序列化
*
* @author tangyi
* @date 2019/3/18 22:37
*/
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {
public CustomOauthExceptionSerializer() {
super(CustomOauthException.class);
}
@Override
public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
// 封装属性,和ResponseBean对应
gen.writeNumberField("status", value.getCode());
// 返回给前端的code
gen.writeNumberField("code", value.getCode());
// 提示信息
gen.writeStringField("msg", value.getMessage());
if (value.getAdditionalInformation() != null) {
for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
String key = entry.getKey();
String add = entry.getValue();
gen.writeStringField(key, add);
}
}
gen.writeEndObject();
}
}
package com.github.tangyi.common.security.wx;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
......@@ -24,6 +27,8 @@ public class WxAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService customUserDetailsService;
private ApplicationEventPublisher publisher;
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
WxAuthenticationToken wxAuthenticationToken = (WxAuthenticationToken) authentication;
......@@ -32,10 +37,12 @@ public class WxAuthenticationProvider implements AuthenticationProvider {
UserDetails userDetails = customUserDetailsService.loadUserByWxCodeAndTenantCode(principal, TenantContextHolder.getTenantCode(), wxAuthenticationToken.getWxUser());
if (userDetails == null) {
log.debug("Authentication failed: no credentials provided");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.noopBindAccount", "Noop Bind Account"));
}
WxAuthenticationToken authenticationToken = new WxAuthenticationToken(userDetails, userDetails.getAuthorities());
authenticationToken.setDetails(wxAuthenticationToken.getDetails());
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
return authenticationToken;
}
......
......@@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import lombok.Data;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.AuthenticationEventPublisher;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
......@@ -27,6 +28,9 @@ public class WxSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecur
@Autowired
private AuthenticationEventPublisher defaultAuthenticationEventPublisher;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
private AuthenticationSuccessHandler wxLoginSuccessHandler;
private CustomUserDetailsService userDetailsService;
......@@ -40,6 +44,7 @@ public class WxSecurityConfigurer extends SecurityConfigurerAdapter<DefaultSecur
wxAuthenticationFilter.setEventPublisher(defaultAuthenticationEventPublisher);
WxAuthenticationProvider wxAuthenticationProvider = new WxAuthenticationProvider();
wxAuthenticationProvider.setCustomUserDetailsService(userDetailsService);
wxAuthenticationProvider.setPublisher(applicationEventPublisher);
// 增加微信登录的过滤器
http.authenticationProvider(wxAuthenticationProvider).addFilterAfter(wxAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
package com.github.tangyi.auth.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
*
* 配置aop
*
* @author tangyi
* @date 2019-11-12 20:13
*/
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
package com.github.tangyi.auth.config;
import com.github.tangyi.auth.filter.CustomTokenEndpointAuthenticationFilter;
import com.github.tangyi.auth.security.CustomTokenConverter;
import com.github.tangyi.common.security.core.ClientDetailsServiceImpl;
import com.github.tangyi.common.security.exceptions.CustomOauthException;
import com.github.tangyi.user.api.feign.UserServiceClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
......@@ -37,11 +30,6 @@ import javax.sql.DataSource;
public class CustomAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {
/**
* 认证管理器
*/
private final AuthenticationManager authenticationManager;
/**
* redis连接工厂
*/
private final RedisConnectionFactory redisConnectionFactory;
......@@ -56,21 +44,13 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
*/
private final KeyProperties keyProperties;
private final UserServiceClient userServiceClient;
private OAuth2RequestFactory oAuth2RequestFactory;
@Autowired
public CustomAuthorizationServerConfigurer(AuthenticationManager authenticationManager,
RedisConnectionFactory redisConnectionFactory,
public CustomAuthorizationServerConfigurer(RedisConnectionFactory redisConnectionFactory,
DataSource dataSource,
KeyProperties keyProperties,
UserServiceClient userServiceClient) {
this.authenticationManager = authenticationManager;
KeyProperties keyProperties) {
this.redisConnectionFactory = redisConnectionFactory;
this.dataSource = dataSource;
this.keyProperties = keyProperties;
this.userServiceClient = userServiceClient;
}
/**
......@@ -123,25 +103,11 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
oAuth2RequestFactory = endpoints.getOAuth2RequestFactory();
endpoints
// 将token存储到redis
.tokenStore(tokenStore())
// token增强
.tokenEnhancer(jwtTokenEnhancer())
// 认证管理器
.authenticationManager(authenticationManager)
// 异常处理
.exceptionTranslator(e -> {
if (e instanceof OAuth2Exception) {
OAuth2Exception oAuth2Exception = (OAuth2Exception) e;
return ResponseEntity
.status(oAuth2Exception.getHttpErrorCode())
.body(new CustomOauthException(oAuth2Exception.getMessage(), oAuth2Exception.getHttpErrorCode()));
} else {
throw e;
}
});
.tokenEnhancer(jwtTokenEnhancer());
}
/**
......@@ -158,7 +124,6 @@ public class CustomAuthorizationServerConfigurer extends AuthorizationServerConf
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
//.addTokenEndpointAuthenticationFilter(new CustomTokenEndpointAuthenticationFilter(authenticationManager, oAuth2RequestFactory, userServiceClient));
}
}
package com.github.tangyi.auth.config;
import com.github.tangyi.auth.error.CustomOAuth2AccessDeniedHandler;
import com.github.tangyi.auth.security.CustomUserDetailsAuthenticationProvider;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
......@@ -12,7 +14,10 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.web.access.AccessDeniedHandler;
/**
* Spring Security配置
......@@ -28,6 +33,12 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Autowired
private AuthorizationServerEndpointsConfiguration endpoints;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
......@@ -35,6 +46,16 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
.csrf().disable()
.authorizeRequests()
.anyRequest().authenticated();
if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) {
UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class);
endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService);
}
// 认证管理器
endpoints.getEndpointsConfigurer().authenticationManager(authenticationManager());
// accessDeniedHandler
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler());
}
@Bean
......@@ -54,7 +75,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
*/
@Bean
public AuthenticationProvider authProvider() {
return new CustomUserDetailsAuthenticationProvider(encoder(), userDetailsService);
return new CustomUserDetailsAuthenticationProvider(encoder(), userDetailsService, applicationEventPublisher);
}
@Override
......@@ -62,5 +83,10 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return new CustomOAuth2AccessDeniedHandler();
}
}
......@@ -9,7 +9,7 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminAuthorization;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
......@@ -113,7 +112,7 @@ public class OauthClientDetailsController extends BaseController {
* @date 2019/03/30 16:57
*/
@PostMapping
@PreAuthorize("hasAuthority('sys:client:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "创建客户端", notes = "创建客户端")
@ApiImplicitParam(name = "oauthClientDetails", value = "客户端实体oauthClientDetails", required = true, dataType = "OauthClientDetails")
@Log("新增客户端")
......@@ -133,7 +132,7 @@ public class OauthClientDetailsController extends BaseController {
* @date 2019/03/30 16:56
*/
@PutMapping
@PreAuthorize("hasAuthority('sys:client:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "更新客户端信息", notes = "根据客户端id更新客户端的基本信息")
@ApiImplicitParam(name = "oauthClientDetails", value = "客户端实体oauthClientDetails", required = true, dataType = "OauthClientDetails")
@Log("修改客户端")
......@@ -155,7 +154,7 @@ public class OauthClientDetailsController extends BaseController {
* @date 2019/03/30 16:59
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:client:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "删除客户端", notes = "根据ID删除客户端")
@ApiImplicitParam(name = "id", value = "客户端ID", required = true, paramType = "path")
@Log("删除客户端")
......@@ -176,7 +175,7 @@ public class OauthClientDetailsController extends BaseController {
* @date 2019/03/30 17:01
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('sys:client:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "批量删除客户端", notes = "根据客户端id批量删除客户端")
@ApiImplicitParam(name = "oauthClientDetails", value = "客户端信息", dataType = "OauthClientDetails")
@Log("批量删除客户端")
......
package com.github.tangyi.auth.error;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.oauth2.provider.error.AbstractOAuth2SecurityExceptionHandler;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.web.context.request.ServletWebRequest;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* accessDeniedHandler,return ResponseBean
*
* @author tangyi
* @date 2019-11-11 21:24
*/
@Slf4j
public class CustomOAuth2AccessDeniedHandler extends AbstractOAuth2SecurityExceptionHandler
implements AccessDeniedHandler {
private WebResponseExceptionTranslator<?> exceptionTranslator = new DefaultWebResponseExceptionTranslator();
private CustomOAuth2ExceptionRenderer exceptionRenderer = new CustomOAuth2ExceptionRenderer();
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
throws IOException {
try {
ResponseEntity<?> result = exceptionTranslator.translate(authException);
result = enhanceResponse(result, authException);
exceptionRenderer.handleResponseBeanResponse(result, new ServletWebRequest(request, response));
response.flushBuffer();
} catch (ServletException e) {
log.error(e.getMessage(), e);
} catch (IOException | RuntimeException e) {
throw e;
} catch (Exception e) {
// Wrap other Exceptions. These are not expected to happen
throw new RuntimeException(e);
}
}
}
package com.github.tangyi.auth.error;
import com.github.tangyi.common.core.model.ResponseBean;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.security.oauth2.http.converter.jaxb.JaxbOAuth2ExceptionMessageConverter;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
*
* render异常
*
* @author tangyi
* @date 2019-11-11 21:29
*/
@Slf4j
public class CustomOAuth2ExceptionRenderer {
private List<HttpMessageConverter<?>> messageConverters = geDefaultMessageConverters();
public void handleResponseBeanResponse(ResponseEntity<?> responseEntity, ServletWebRequest webRequest)
throws Exception {
if (responseEntity == null) {
return;
}
HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
if (outputMessage instanceof ServerHttpResponse) {
((ServerHttpResponse) outputMessage).setStatusCode(responseEntity.getStatusCode());
}
HttpHeaders entityHeaders = responseEntity.getHeaders();
if (!entityHeaders.isEmpty()) {
outputMessage.getHeaders().putAll(entityHeaders);
}
Object body = responseEntity.getBody();
if (body != null) {
// 返回ResponseBean
ResponseBean<?> responseBean = new ResponseBean<>(body);
responseBean.setStatus(responseEntity.getStatusCode().value());
writeWithMessageConverters(responseBean, inputMessage, outputMessage);
} else {
// flush headers
outputMessage.getBody();
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void writeWithMessageConverters(Object returnValue, HttpInputMessage inputMessage,
HttpOutputMessage outputMessage) throws IOException, HttpMediaTypeNotAcceptableException {
List<MediaType> acceptedMediaTypes = inputMessage.getHeaders().getAccept();
if (acceptedMediaTypes.isEmpty()) {
acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
}
MediaType.sortByQualityValue(acceptedMediaTypes);
Class<?> returnValueType = returnValue.getClass();
List<MediaType> allSupportedMediaTypes = new ArrayList<>();
for (MediaType acceptedMediaType : acceptedMediaTypes) {
for (HttpMessageConverter messageConverter : messageConverters) {
if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
messageConverter.write(returnValue, acceptedMediaType, outputMessage);
if (log.isDebugEnabled()) {
MediaType contentType = outputMessage.getHeaders().getContentType();
if (contentType == null) {
contentType = acceptedMediaType;
}
log.debug("Written [" + returnValue + "] as \"" + contentType + "\" using [" + messageConverter
+ "]");
}
return;
}
}
}
for (HttpMessageConverter messageConverter : messageConverters) {
allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
}
throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
}
private List<HttpMessageConverter<?>> geDefaultMessageConverters() {
List<HttpMessageConverter<?>> result = new ArrayList<>(new RestTemplate().getMessageConverters());
result.add(new JaxbOAuth2ExceptionMessageConverter());
return result;
}
private HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) {
return new ServletServerHttpRequest(webRequest.getNativeRequest(HttpServletRequest.class));
}
private HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) {
return new ServletServerHttpResponse((HttpServletResponse) webRequest.getNativeResponse());
}
}
package com.github.tangyi.auth.filter;
import com.github.tangyi.auth.model.CustomUserDetails;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.ServiceConstant;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.provider.OAuth2RequestFactory;
import org.springframework.security.oauth2.provider.endpoint.TokenEndpointAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
*
* 登录成功后的处理,如记录登录日志
*
* @author tangyi
* @date 2019-10-11 12:08
*/
@Slf4j
public class CustomTokenEndpointAuthenticationFilter extends TokenEndpointAuthenticationFilter {
private UserServiceClient userServiceClient;
public CustomTokenEndpointAuthenticationFilter(AuthenticationManager authenticationManager,
OAuth2RequestFactory oAuth2RequestFactory, UserServiceClient userServiceClient) {
super(authenticationManager, oAuth2RequestFactory);
this.userServiceClient = userServiceClient;
}
@Override
protected void onSuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
// 登录成功后的处理
log.info("CustomTokenEndpointAuthenticationFilter onSuccessfulAuthentication");
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
String tenantCode = userDetails.getTenantCode();
String username = userDetails.getUsername();
log.info("CustomTokenEndpointAuthenticationFilter 登录成功, tenantCode: {}, username: {}", tenantCode, username);
// 记录日志
Log logInfo = new Log();
logInfo.setCommonValue(username, SysUtil.getSysCode(), tenantCode);
logInfo.setTitle("用户登录");
logInfo.setType(CommonConstant.STATUS_NORMAL);
logInfo.setMethod(request.getMethod());
logInfo.setTime(String.valueOf(System.currentTimeMillis() - userDetails.getStart()));
logInfo.setRequestUri(request.getRequestURI());
// 获取ip、浏览器信息
logInfo.setIp(request.getRemoteAddr());
logInfo.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
logInfo.setServiceId(ServiceConstant.AUTH_SERVICE);
// 记录日志
saveLoginSuccessLog(logInfo);
}
@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
// 认证前的特殊处理
// if (!condition) {
// throw new AuthenticationServiceException("condition not satisfied");
// }
log.info("CustomTokenEndpointAuthenticationFilter doFilter");
super.doFilter(req, res, chain);
}
/**
* 异步记录登录日志
*
* @author tangyi
* @date 2019/05/30 23:30
*/
@Async
public void saveLoginSuccessLog(Log logInfo) {
try {
userServiceClient.saveLog(logInfo);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
package com.github.tangyi.auth.listener;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
/**
*
* 处理登录失败事件
*
* @author tangyi
* @date 2019-11-11 23:52
*/
@Slf4j
@AllArgsConstructor
@Component
public class LoginFailureListener implements ApplicationListener<CustomAuthenticationFailureEvent> {
private final UserServiceClient userServiceClient;
@Override
public void onApplicationEvent(CustomAuthenticationFailureEvent event) {
// 登录失败后的处理
}
}
package com.github.tangyi.auth.listener;
import com.github.tangyi.auth.model.CustomUserDetails;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.constant.ServiceConstant;
import com.github.tangyi.common.core.model.Log;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.user.api.dto.UserDto;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationListener;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
/**
*
* 处理登录成功事件
*
* @author tangyi
* @date 2019-11-11 23:48
*/
@Slf4j
@AllArgsConstructor
@Component
public class LoginSuccessListener implements ApplicationListener<CustomAuthenticationSuccessEvent> {
private final UserServiceClient userServiceClient;
@Override
public void onApplicationEvent(CustomAuthenticationSuccessEvent event) {
// 登录成功后的处理
UserDetails userDetails = event.getUserDetails();
if (userDetails instanceof CustomUserDetails) {
CustomUserDetails customUserDetails = (CustomUserDetails) userDetails;
String tenantCode = customUserDetails.getTenantCode();
String username = userDetails.getUsername();
log.info("{} login success, tenantCode: {}", username, tenantCode);
// 记录日志
Log logInfo = new Log();
logInfo.setTitle("用户登录");
logInfo.setCommonValue(username, SysUtil.getSysCode(), tenantCode);
logInfo.setTime(String.valueOf(System.currentTimeMillis() - customUserDetails.getStart()));
logInfo.setType(CommonConstant.STATUS_NORMAL);
HttpServletRequest request = currentRequestAttributes().getRequest();
logInfo.setMethod(request.getMethod());
logInfo.setRequestUri(request.getRequestURI());
// 获取ip、浏览器信息
logInfo.setIp(request.getRemoteAddr());
logInfo.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
logInfo.setServiceId(ServiceConstant.AUTH_SERVICE);
// 记录日志和登录时间
UserDto userDto = new UserDto();
userDto.setIdentifier(username);
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
saveLoginInfo(logInfo, userDto);
}
}
/**
* 异步记录登录日志
*
* @param logInfo logInfo
* @param userDto userDto
* @author tangyi
* @date 2019/05/30 23:30
*/
@Async
public void saveLoginInfo(Log logInfo, UserDto userDto) {
try {
userServiceClient.saveLog(logInfo);
//userServiceClient.updateUser(userDto);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
/**
*
* 获取当前request
*
* @return ServletRequestAttributes
* @author tangyi
* @date 2019-11-12 00:15
*/
private static ServletRequestAttributes currentRequestAttributes() {
RequestAttributes requestAttr = RequestContextHolder.currentRequestAttributes();
if (!(requestAttr instanceof ServletRequestAttributes)) {
throw new IllegalStateException("Current request is not a servlet request");
}
return (ServletRequestAttributes) requestAttr;
}
}
package com.github.tangyi.auth.security;
import com.github.tangyi.common.security.event.CustomAuthenticationFailureEvent;
import com.github.tangyi.common.security.event.CustomAuthenticationSuccessEvent;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.tenant.TenantContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
......@@ -30,9 +33,12 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
private String userNotFoundEncodedPassword;
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService) {
private ApplicationEventPublisher publisher;
public CustomUserDetailsAuthenticationProvider(PasswordEncoder passwordEncoder, CustomUserDetailsService userDetailsService, ApplicationEventPublisher publisher) {
this.passwordEncoder = passwordEncoder;
this.userDetailsService = userDetailsService;
this.publisher = publisher;
}
@Override
......@@ -46,8 +52,10 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
// 匹配密码
if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
log.debug("认证失败: 密码错误.");
publisher.publishEvent(new CustomAuthenticationFailureEvent(authentication, userDetails));
throw new BadCredentialsException(messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "密码错误."));
}
publisher.publishEvent(new CustomAuthenticationSuccessEvent(authentication, userDetails));
}
@Override
......@@ -70,9 +78,9 @@ public class CustomUserDetailsAuthenticationProvider extends AbstractUserDetails
UserDetails loadedUser;
try {
// 加载用户信息
loadedUser = this.userDetailsService.loadUserByIdentifierAndTenantCode(authentication.getPrincipal().toString(), TenantContextHolder.getTenantCode());
loadedUser = this.userDetailsService.loadUserByIdentifierAndTenantCode(TenantContextHolder.getTenantCode(), authentication.getPrincipal().toString());
} catch (TenantNotFoundException tenantNotFound) {
throw tenantNotFound;
throw new InternalAuthenticationServiceException(tenantNotFound.getMessage(), tenantNotFound);
} catch (UsernameNotFoundException notFound) {
if (authentication.getCredentials() != null) {
String presentedPassword = authentication.getCredentials().toString();
......
......@@ -9,24 +9,18 @@ import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.exceptions.ServiceException;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.properties.SysProperties;
import com.github.tangyi.common.core.utils.DateUtils;
import com.github.tangyi.common.core.utils.ResponseUtil;
import com.github.tangyi.common.core.vo.RoleVo;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.security.core.CustomUserDetailsService;
import com.github.tangyi.common.security.core.GrantedAuthorityImpl;
import com.github.tangyi.common.security.mobile.MobileUser;
import com.github.tangyi.common.security.wx.WxUser;
import com.github.tangyi.user.api.constant.MenuConstant;
import com.github.tangyi.user.api.dto.UserDto;
import com.github.tangyi.user.api.enums.IdentityType;
import com.github.tangyi.user.api.feign.UserServiceClient;
import com.github.tangyi.user.api.module.Menu;
import com.github.tangyi.user.api.module.Tenant;
import com.google.common.collect.Lists;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.core.GrantedAuthority;
......@@ -35,8 +29,6 @@ import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
......@@ -52,21 +44,19 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
private final UserServiceClient userServiceClient;
private final SysProperties sysProperties;
private final WxSessionService wxService;
/**
* 加载用户信息
*
* @param tenantCode 租户标识
* @param username 用户名
* @return UserDetails
* @throws UsernameNotFoundException,TenantNotFoundException
*/
@Override
public UserDetails loadUserByIdentifierAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException {
public UserDetails loadUserByIdentifierAndTenantCode(String tenantCode, String username) throws UsernameNotFoundException, TenantNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode);
ResponseBean<UserVo> userVoResponseBean = userServiceClient.findUserByIdentifier(username, tenantCode);
if (!ResponseUtil.isSuccess(userVoResponseBean))
throw new ServiceException("查询用户信息失败: " + userVoResponseBean.getMsg());
......@@ -79,17 +69,16 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
/**
* 根据社交账号查询
*
* @param social social
* @param tenantCode tenantCode
* @param social social
* @param mobileUser mobileUser
* @return UserDetails
* @author tangyi
* @date 2019/06/22 21:08
*/
@Override
public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode, MobileUser mobileUser) throws UsernameNotFoundException {
public UserDetails loadUserBySocialAndTenantCode(String tenantCode, String social, MobileUser mobileUser) throws UsernameNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode);
ResponseBean<UserVo> userVoResponseBean = userServiceClient.findUserByIdentifier(social, IdentityType.PHONE_NUMBER.getValue(), tenantCode);
if (!ResponseUtil.isSuccess(userVoResponseBean))
throw new ServiceException("查询用户信息失败: " + userVoResponseBean.getMsg());
......@@ -113,12 +102,6 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
if (!ResponseUtil.isSuccess(userVoResponseBean))
throw new ServiceException("查询用户信息失败: " + userVoResponseBean.getMsg());
userVo = userVoResponseBean.getData();
} else {
// TODO 记录登录时间,IP等信息
UserDto userDto = new UserDto();
BeanUtils.copyProperties(userVo, userDto);
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
//userServiceClient.updateUser(userDto);
}
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.SMS);
}
......@@ -127,17 +110,16 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
* 根据微信code和租户标识查询
* 将code换成openId和sessionKey
*
* @param code code
* @param tenantCode tenantCode
* @param code code
* @param wxUser wxUser
* @return UserDetails
* @author tangyi
* @date 2019/07/05 20:05:36
*/
@Override
public UserDetails loadUserByWxCodeAndTenantCode(String code, String tenantCode, WxUser wxUser) throws UsernameNotFoundException {
public UserDetails loadUserByWxCodeAndTenantCode(String tenantCode, String code, WxUser wxUser) throws UsernameNotFoundException {
long start = System.currentTimeMillis();
Tenant tenant = this.validateTenantCode(tenantCode);
// 根据code获取openId和sessionKey
WxSession wxSession = wxService.code2Session(code);
if (wxSession == null)
......@@ -166,37 +148,11 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
if (!ResponseUtil.isSuccess(userVoResponseBean))
throw new ServiceException("查询用户信息失败: " + userVoResponseBean.getMsg());
userVo = userVoResponseBean.getData();
} else {
// TODO 更新sessionKey,记录登录时间,IP等信息
UserDto userDto = new UserDto();
BeanUtils.copyProperties(userVo, userDto);
//userDto.setCredential(wxSession.getSessionKey());
userDto.setLoginTime(DateUtils.asDate(LocalDateTime.now()));
//userServiceClient.updateUser(userDto);
}
return new CustomUserDetails(userVo.getIdentifier(), userVo.getCredential(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode(), start, LoginType.WECHAT);
}
/**
* 校验租户标识
*
* @param tenantCode tenantCode
* @return Tenant
*/
private Tenant validateTenantCode(String tenantCode) throws TenantNotFoundException {
if (StringUtils.isBlank(tenantCode))
throw new TenantNotFoundException("租户code不能为空.");
// 先获取租户信息
ResponseBean<Tenant> tenantResponseBean = userServiceClient.findTenantByTenantCode(tenantCode);
if (!ResponseUtil.isSuccess(tenantResponseBean))
throw new ServiceException("查询租户信息失败: " + tenantResponseBean.getMsg());
Tenant tenant = tenantResponseBean.getData();
if (tenant == null)
throw new TenantNotFoundException("租户不存在.");
return tenant;
}
/**
* 获取用户权限
*
* @param userVo userVo
......@@ -205,43 +161,9 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
* @date 2019/03/17 14:41
*/
private Set<GrantedAuthority> getAuthority(UserVo userVo) {
// 权限集合
Set<GrantedAuthority> authorities = new HashSet<>();
// 根据角色查找菜单权限
List<Menu> menus = Lists.newArrayList();
// 判断是否是管理员,是则查找所有菜单权限
if (userVo.getIdentifier().equals(sysProperties.getAdminUser())) {
// 查找所有菜单权限,因为角色一般是一个,这里只会执行一次
ResponseBean<List<Menu>> menusResponseBean = userServiceClient.findAllMenu(userVo.getTenantCode());
if (!ResponseUtil.isSuccess(menusResponseBean)) {
throw new ServiceException("查询所有菜单失败: " + menusResponseBean.getMsg());
}
menus = menusResponseBean.getData();
} else {
// 根据角色查询菜单权限
List<RoleVo> roleList = userVo.getRoleList();
if (CollectionUtils.isNotEmpty(roleList)) {
for (RoleVo role : roleList) {
// 根据角色查找菜单权限
ResponseBean<List<Menu>> roleMenusResponseBean = userServiceClient.findMenuByRole(role.getRoleCode(), userVo.getTenantCode());
if (!ResponseUtil.isSuccess(roleMenusResponseBean)) {
throw new ServiceException("查询角色菜单失败: " + roleMenusResponseBean.getMsg());
}
List<Menu> roleMenus = roleMenusResponseBean.getData();
if (CollectionUtils.isNotEmpty(roleMenus))
menus.addAll(roleMenus);
// 权限如果前缀是ROLE_,security就会认为这是个角色信息,而不是权限,例如ROLE_ADMIN就是ADMIN角色,MENU:ADD就是MENU:ADD权限
authorities.add(new GrantedAuthorityImpl(role.getRoleCode()));
}
}
}
if (CollectionUtils.isNotEmpty(menus)) {
// 菜单权限
List<GrantedAuthority> authorityList = menus.stream()
.filter(menu -> MenuConstant.MENU_TYPE_PERMISSION.equals(menu.getType()))
.map(menu -> new GrantedAuthorityImpl(menu.getPermission())).collect(Collectors.toList());
authorities.addAll(authorityList);
}
return authorities;
return userVo.getRoleList()
.stream()
.map(role -> new GrantedAuthorityImpl(role.getRoleCode().toUpperCase()))
.collect(Collectors.toSet());
}
}
package com.github.tangyi.auth.security;
import com.github.tangyi.common.core.exceptions.ServiceException;
import com.github.tangyi.common.core.exceptions.TenantNotFoundException;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.ResponseUtil;
import com.github.tangyi.user.api.feign.UserServiceClient;
import com.github.tangyi.user.api.module.Tenant;
import lombok.AllArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
*
* 校验租户
*
* @author tangyi
* @date 2019-11-12 20:14
*/
@AllArgsConstructor
@Aspect
@Component
public class ValidateTenantAspect {
private final UserServiceClient userServiceClient;
@Before("execution(* com.github.tangyi.auth.security.CustomUserDetailsServiceImpl.load*(..)) && args(tenantCode,..)")
public void validateTenantCode(String tenantCode) throws TenantNotFoundException {
// 获取tenantCode
if (StringUtils.isBlank(tenantCode))
throw new TenantNotFoundException("租户code不能为空.");
// 先获取租户信息
ResponseBean<Tenant> tenantResponseBean = userServiceClient.findTenantByTenantCode(tenantCode);
if (!ResponseUtil.isSuccess(tenantResponseBean))
throw new ServiceException("查询租户信息失败: " + tenantResponseBean.getMsg());
Tenant tenant = tenantResponseBean.getData();
if (tenant == null)
throw new TenantNotFoundException("租户不存在.");
}
}
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.exam.api.module.Course;
import com.github.tangyi.exam.service.CourseService;
import io.swagger.annotations.Api;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -93,7 +92,7 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:31
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:course:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建课程", notes = "创建课程")
@ApiImplicitParam(name = "course", value = "课程实体course", required = true, dataType = "Course")
@Log("新增课程")
......@@ -111,7 +110,7 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:31
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:course:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新课程信息", notes = "根据课程id更新课程的基本信息")
@ApiImplicitParam(name = "course", value = "课程实体course", required = true, dataType = "Course")
@Log("更新课程")
......@@ -129,7 +128,7 @@ public class CourseController extends BaseController {
* @date 2018/11/10 21:32
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除课程", notes = "根据ID删除课程")
@ApiImplicitParam(name = "id", value = "课程ID", required = true, paramType = "path")
@Log("删除课程")
......@@ -158,7 +157,7 @@ public class CourseController extends BaseController {
* @date 2018/12/4 11:26
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:course:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "批量删除课程", notes = "根据课程id批量删除课程")
@ApiImplicitParam(name = "ids", value = "课程ID", dataType = "Long")
@Log("批量删除课程")
......
......@@ -4,11 +4,9 @@ import com.github.pagehelper.PageInfo;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.vo.DeptVo;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.exam.api.dto.ExaminationRecordDto;
import com.github.tangyi.exam.api.dto.StartExamDto;
import com.github.tangyi.exam.api.enums.SubmitStatusEnum;
......@@ -18,7 +16,6 @@ import com.github.tangyi.exam.service.AnswerService;
import com.github.tangyi.exam.service.ExamRecordService;
import com.github.tangyi.exam.service.ExaminationService;
import com.github.tangyi.exam.utils.ExamRecordUtil;
import com.github.tangyi.user.api.feign.UserServiceClient;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
......@@ -29,7 +26,6 @@ import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
......@@ -58,8 +54,6 @@ public class ExamRecordController extends BaseController {
private final ExaminationService examinationService;
private final UserServiceClient userServiceClient;
private final AnswerService answerService;
/**
......@@ -113,9 +107,6 @@ public class ExamRecordController extends BaseController {
if (CollectionUtils.isNotEmpty(examRecordPageInfo.getList())) {
// 查询考试信息
List<Examination> examinations = examinationService.findListById(examRecordPageInfo.getList().stream().map(ExaminationRecord::getExaminationId).distinct().toArray(Long[]::new));
// 查询用户信息
ResponseBean<List<UserVo>> returnT = userServiceClient.findUserById(examRecordPageInfo.getList().stream().map(ExaminationRecord::getUserId).distinct().toArray(Long[]::new));
if (returnT != null && CollectionUtils.isNotEmpty(returnT.getData())) {
examRecordPageInfo.getList().forEach(tempExamRecord -> {
// 找到考试记录所属的考试信息
Examination examinationRecordExamination = examinations.stream()
......@@ -139,29 +130,7 @@ public class ExamRecordController extends BaseController {
examRecordDtoList.add(examRecordDto);
}
});
// 查询部门信息
ResponseBean<List<DeptVo>> deptResponseBean = userServiceClient.findDeptById(returnT.getData().stream().map(UserVo::getDeptId).distinct().toArray(Long[]::new));
examRecordDtoList.forEach(tempExamRecordDto -> {
// 查询、设置用户信息
UserVo examRecordDtoUserVo = returnT.getData().stream()
.filter(tempUserVo -> tempExamRecordDto.getUserId().equals(tempUserVo.getId()))
.findFirst().orElse(null);
if (examRecordDtoUserVo != null) {
// 设置用户名
tempExamRecordDto.setUserName(examRecordDtoUserVo.getName());
// 查询、设置部门信息
if (deptResponseBean != null && CollectionUtils.isNotEmpty(deptResponseBean.getData())) {
DeptVo examRecordDtoDeptVo = deptResponseBean.getData().stream()
// 根据部门ID过滤
.filter(tempDept -> tempDept.getId().equals(examRecordDtoUserVo.getDeptId()))
.findFirst().orElse(null);
// 设置部门名称
if (examRecordDtoDeptVo != null)
tempExamRecordDto.setDeptName(examRecordDtoDeptVo.getDeptName());
}
}
});
}
examRecordService.fillExamUserInfo(examRecordDtoList, examRecordPageInfo.getList().stream().map(ExaminationRecord::getUserId).distinct().toArray(Long[]::new));
}
examRecordDtoPageInfo.setTotal(examRecordPageInfo.getTotal());
examRecordDtoPageInfo.setPages(examRecordPageInfo.getPages());
......@@ -184,10 +153,6 @@ public class ExamRecordController extends BaseController {
@ApiImplicitParam(name = "examRecord", value = "考试记录实体examRecord", required = true, dataType = "ExamRecord")
@Log("新增考试记录")
public ResponseBean<ExaminationRecord> addExamRecord(@RequestBody @Valid ExaminationRecord examRecord) {
Examination examination = new Examination();
examination.setId(examRecord.getExaminationId());
// 查找考试信息
examination = examinationService.get(examination);
examRecord.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
examRecord.setStartTime(examRecord.getCreateDate());
examRecordService.insert(examRecord);
......@@ -245,7 +210,7 @@ public class ExamRecordController extends BaseController {
* @date 2018/12/31 22:28
*/
@PostMapping("export")
@PreAuthorize("hasAuthority('exam:examRecord:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导出考试成绩", notes = "根据成绩id导出成绩")
@ApiImplicitParam(name = "ids", value = "成绩ID", required = true, dataType = "Long")
@Log("导出考试记录")
......@@ -292,30 +257,7 @@ public class ExamRecordController extends BaseController {
examRecordDtoList.add(recordDto);
}
});
// 查询用户信息
ResponseBean<List<UserVo>> returnT = userServiceClient.findUserById(userIdSet.toArray(new Long[0]));
if (returnT != null && CollectionUtils.isNotEmpty(returnT.getData())) {
// 获取部门信息
ResponseBean<List<DeptVo>> deptResponseBean = userServiceClient.findDeptById(returnT.getData().stream().map(UserVo::getDeptId).distinct().toArray(Long[]::new));
examRecordDtoList.forEach(tempExamRecordDto -> {
// 查询用户信息
UserVo examRecordDtoUserVo = returnT.getData().stream().filter(tempUserVo -> tempExamRecordDto.getUserId().equals(tempUserVo.getId()))
.findFirst().orElse(null);
if (examRecordDtoUserVo != null) {
tempExamRecordDto.setUserName(examRecordDtoUserVo.getName());
// 查询部门信息
if (deptResponseBean != null && CollectionUtils.isNotEmpty(deptResponseBean.getData())) {
// 查找用户所属部门
DeptVo examRecordDtoDeptVo = deptResponseBean.getData().stream()
.filter(tempDept -> tempDept.getId().equals(examRecordDtoUserVo.getDeptId()))
.findFirst().orElse(null);
// 设置所属部门名称
if (examRecordDtoDeptVo != null)
tempExamRecordDto.setDeptName(examRecordDtoDeptVo.getDeptName());
}
}
});
}
examRecordService.fillExamUserInfo(examRecordDtoList, userIdSet.toArray(new Long[0]));
// 导出
ExcelToolUtil.exportExcel(request.getInputStream(), response.getOutputStream(), MapUtil.java2Map(examRecordDtoList), ExamRecordUtil.getExamRecordDtoMap());
}
......
......@@ -7,8 +7,7 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.exam.api.dto.AnswerCartDto;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.exam.api.dto.ExaminationDto;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.module.Course;
......@@ -25,7 +24,6 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -152,7 +150,7 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:14
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:exam:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建考试", notes = "创建考试")
@ApiImplicitParam(name = "examinationDto", value = "考试实体examinationDto", required = true, dataType = "ExaminationDto")
@Log("新增考试")
......@@ -173,7 +171,7 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:15
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:exam:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新考试信息", notes = "根据考试id更新考试的基本信息")
@ApiImplicitParam(name = "examinationDto", value = "考试实体answer", required = true, dataType = "ExaminationDto")
@Log("更新考试")
......@@ -195,7 +193,7 @@ public class ExaminationController extends BaseController {
* @date 2018/11/10 21:20
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除考试", notes = "根据ID删除考试")
@ApiImplicitParam(name = "id", value = "考试ID", required = true, paramType = "path")
@Log("删除考试")
......@@ -224,7 +222,7 @@ public class ExaminationController extends BaseController {
* @date 2018/12/03 22:03
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:exam:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "批量删除考试", notes = "根据考试id批量删除考试")
@ApiImplicitParam(name = "ids", value = "考试ID", dataType = "Long")
@Log("批量删除考试")
......
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.utils.TreeUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.exam.api.constants.ExamSubjectConstant;
import com.github.tangyi.exam.api.dto.SubjectCategoryDto;
import com.github.tangyi.exam.api.module.SubjectCategory;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -89,7 +88,7 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:00
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:subject:category:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建分类", notes = "创建分类")
@ApiImplicitParam(name = "subjectCategory", value = "分类实体subjectCategory", required = true, dataType = "SubjectCategory")
@Log("新增题目分类")
......@@ -108,7 +107,7 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:01
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:subject:category:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新分类信息", notes = "根据分类id更新分类的基本信息")
@ApiImplicitParam(name = "subjectCategory", value = "分类实体subjectCategory", required = true, dataType = "SubjectCategory")
@Log("更新题目分类")
......@@ -126,7 +125,7 @@ public class SubjectCategoryController extends BaseController {
* @date 2018/12/04 22:02
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('exam:subject:category:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除分类", notes = "根据ID删除分类")
@ApiImplicitParam(name = "id", value = "分类ID", required = true, paramType = "path")
@Log("删除题目分类")
......
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.service.AnswerService;
import com.github.tangyi.exam.service.SubjectService;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
......@@ -101,7 +100,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@PostMapping
@PreAuthorize("hasAuthority('exam:exam:subject:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建题目", notes = "创建题目")
@ApiImplicitParam(name = "subject", value = "题目信息", required = true, dataType = "SubjectDto")
@Log("新增题目")
......@@ -119,7 +118,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@PutMapping
@PreAuthorize("hasAuthority('exam:exam:subject:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新题目信息", notes = "根据题目id更新题目的基本信息")
@ApiImplicitParam(name = "subject", value = "角色实体subject", required = true, dataType = "Subject")
@Log("更新题目")
......@@ -137,7 +136,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/10 21:43
*/
@DeleteMapping("{id}")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除题目", notes = "根据ID删除题目")
@ApiImplicitParams({
@ApiImplicitParam(name = "id", value = "题目ID", required = true, dataType = "Long", paramType = "path"),
......@@ -161,7 +160,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/28 12:53
*/
@PostMapping("export")
@PreAuthorize("hasAuthority('exam:exam:subject:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导出题目", notes = "根据分类id导出题目")
@ApiImplicitParams({
@ApiImplicitParam(name = "ids", value = "题目ID", required = true, dataType = "Long"),
......@@ -200,7 +199,7 @@ public class SubjectController extends BaseController {
* @date 2018/11/28 12:59
*/
@RequestMapping("import")
@PreAuthorize("hasAuthority('exam:exam:subject:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导入题目", notes = "导入题目")
@ApiImplicitParams({
@ApiImplicitParam(name = "examinationId", value = "考试ID", dataType = "Long"),
......@@ -232,7 +231,7 @@ public class SubjectController extends BaseController {
* @date 2018/12/04 9:55
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('exam:exam:subject:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "批量删除题目", notes = "根据题目id批量删除题目")
@ApiImplicitParam(name = "ids", value = "题目ID", dataType = "Long")
@Log("批量删除题目")
......
package com.github.tangyi.exam.service;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.ResponseUtil;
import com.github.tangyi.common.core.vo.DeptVo;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.exam.api.dto.ExaminationRecordDto;
import com.github.tangyi.exam.api.module.ExaminationRecord;
import com.github.tangyi.exam.mapper.ExamRecordMapper;
import com.github.tangyi.user.api.feign.UserServiceClient;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 考试记录service
*
......@@ -20,6 +29,8 @@ import org.springframework.transaction.annotation.Transactional;
@Service
public class ExamRecordService extends CrudService<ExamRecordMapper, ExaminationRecord> {
private final UserServiceClient userServiceClient;
/**
* 查询考试记录
*
......@@ -90,4 +101,40 @@ public class ExamRecordService extends CrudService<ExamRecordMapper, Examination
public int deleteAll(Long[] ids) {
return super.deleteAll(ids);
}
/**
* 获取用户、部门相关信息
* @param examRecordDtoList examRecordDtoList
* @param userIds userIds
*/
public void fillExamUserInfo(List<ExaminationRecordDto> examRecordDtoList, Long[] userIds) {
// 查询用户信息
ResponseBean<List<UserVo>> returnT = userServiceClient.findUserById(userIds);
if (ResponseUtil.isSuccess(returnT)) {
// 查询部门信息
ResponseBean<List<DeptVo>> deptResponseBean = userServiceClient.findDeptById(returnT.getData().stream().map(UserVo::getDeptId).distinct().toArray(Long[]::new));
if (ResponseUtil.isSuccess(deptResponseBean)) {
examRecordDtoList.forEach(tempExamRecordDto -> {
// 查询、设置用户信息
UserVo examRecordDtoUserVo = returnT.getData().stream()
.filter(tempUserVo -> tempExamRecordDto.getUserId().equals(tempUserVo.getId()))
.findFirst().orElse(null);
if (examRecordDtoUserVo != null) {
// 设置用户名
tempExamRecordDto.setUserName(examRecordDtoUserVo.getName());
// 查询、设置部门信息
if (CollectionUtils.isNotEmpty(deptResponseBean.getData())) {
DeptVo examRecordDtoDeptVo = deptResponseBean.getData().stream()
// 根据部门ID过滤
.filter(tempDept -> tempDept.getId().equals(examRecordDtoUserVo.getDeptId()))
.findFirst().orElse(null);
// 设置部门名称
if (examRecordDtoDeptVo != null)
tempExamRecordDto.setDeptName(examRecordDtoDeptVo.getDeptName());
}
}
});
}
}
}
}
......@@ -5,7 +5,6 @@ import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.exam.api.dto.AnswerCartDto;
import com.github.tangyi.exam.api.dto.SubjectDto;
import com.github.tangyi.exam.api.module.Examination;
import com.github.tangyi.exam.api.module.ExaminationSubject;
......
......@@ -23,7 +23,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 题目service
......
......@@ -45,5 +45,28 @@ public class MenuConstant {
* 修改
*/
public static final String PERMISSION_SUFFIX_MODIFY = ":edit";
public static final String MENU_SYS = "sys";
/**
* 终端管理
*/
public static final String MENU_CLIENT = "sys:client";
/**
* 路由管理
*/
public static final String MENU_ROUTE = "sys:route";
/**
* 租户中心
*/
public static final String MENU_TENANT = "tenant";
/**
* 系统监控
*/
public static final String MENU_MONITOR = "monitor";
}
......@@ -8,7 +8,7 @@ import com.github.tangyi.common.core.utils.TreeUtil;
import com.github.tangyi.common.core.vo.DeptVo;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.user.api.dto.DeptDto;
import com.github.tangyi.user.api.module.Dept;
import com.github.tangyi.user.service.DeptService;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -91,7 +90,7 @@ public class DeptController extends BaseController {
* @date 2018/8/28 10:15
*/
@PostMapping
@PreAuthorize("hasAuthority('sys:dept:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建部门", notes = "创建部门")
@ApiImplicitParam(name = "dept", value = "部门实体", required = true, dataType = "Dept")
@Log("新增部门")
......@@ -109,7 +108,7 @@ public class DeptController extends BaseController {
* @date 2018/8/28 10:16
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:dept:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除部门", notes = "根据ID删除部门")
@ApiImplicitParam(name = "id", value = "部门ID", required = true, paramType = "path")
@Log("删除部门")
......@@ -129,7 +128,7 @@ public class DeptController extends BaseController {
* @date 2018/8/28 10:22
*/
@PutMapping
@PreAuthorize("hasAuthority('sys:dept:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新部门信息", notes = "根据部门id更新部门的基本信息")
@ApiImplicitParam(name = "dept", value = "部门实体", required = true, dataType = "Dept")
@Log("更新部门")
......
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminAuthorization;
import com.github.tangyi.user.service.LogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
......@@ -16,7 +16,6 @@ import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -98,10 +97,6 @@ public class LogController extends BaseController {
@ApiOperation(value = "新增日志", notes = "新增日志")
@ApiImplicitParam(name = "log", value = "日志实体Log", required = true, dataType = "Log")
public ResponseBean<Boolean> addLog(@RequestBody @Valid Log log) {
if (log.getId() != null)
log.setCommonValue(SysUtil.getUser(), SysUtil.getSysCode(), SysUtil.getTenantCode());
if (true)
return null;
// 保存日志
return new ResponseBean<>(logService.insert(log) > 0);
}
......@@ -115,7 +110,7 @@ public class LogController extends BaseController {
* @date 2018/10/31 21:27
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('monitor:log:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "删除日志", notes = "根据ID删除日志")
@ApiImplicitParam(name = "id", value = "日志ID", required = true, paramType = "path")
public ResponseBean<Boolean> delete(@PathVariable Long id) {
......@@ -133,7 +128,7 @@ public class LogController extends BaseController {
* @date 2018/12/4 10:12
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('monitor:log:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminAuthorization
@ApiOperation(value = "批量删除日志", notes = "根据日志ids批量删除日志")
@ApiImplicitParam(name = "ids", value = "日志ID", dataType = "Long")
public ResponseBean<Boolean> deleteAllLog(@RequestBody Long[] ids) {
......
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.user.api.dto.MenuDto;
import com.github.tangyi.user.api.module.Menu;
import com.github.tangyi.user.service.MenuService;
......@@ -17,7 +17,6 @@ import io.swagger.annotations.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
......@@ -90,7 +89,7 @@ public class MenuController extends BaseController {
* @date 2018/8/27 16:12
*/
@PostMapping
@PreAuthorize("hasAuthority('sys:menu:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建菜单", notes = "创建菜单")
@ApiImplicitParam(name = "menu", value = "角色实体menu", required = true, dataType = "Menu")
@Log("新增菜单")
......@@ -108,7 +107,7 @@ public class MenuController extends BaseController {
* @date 2018/10/24 16:34
*/
@PutMapping
@PreAuthorize("hasAuthority('sys:menu:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新菜单信息", notes = "根据菜单id更新菜单的基本信息")
@ApiImplicitParam(name = "menu", value = "角色实体menu", required = true, dataType = "Menu")
@Log("更新菜单")
......@@ -126,7 +125,7 @@ public class MenuController extends BaseController {
* @date 2018/8/27 16:19
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:menu:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除菜单", notes = "根据ID删除菜单")
@ApiImplicitParam(name = "id", value = "菜单ID", required = true, paramType = "path")
@Log("删除菜单")
......@@ -243,7 +242,7 @@ public class MenuController extends BaseController {
* @date 2018/11/28 12:46
*/
@PostMapping("export")
@PreAuthorize("hasAuthority('sys:menu:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导出菜单", notes = "根据菜单id导出菜单")
@ApiImplicitParam(name = "ids", value = "菜单ID", required = true, dataType = "Long")
@Log("导出菜单")
......@@ -280,7 +279,7 @@ public class MenuController extends BaseController {
* @date 2018/11/28 12:51
*/
@PostMapping("import")
@PreAuthorize("hasAuthority('sys:menu:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导入菜单", notes = "导入菜单")
@Log("导入菜单")
public ResponseBean<Boolean> importMenu(@ApiParam(value = "要上传的文件", required = true) MultipartFile file) {
......
......@@ -7,7 +7,7 @@ import com.github.tangyi.common.core.utils.PageUtil;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.user.api.module.Role;
import com.github.tangyi.user.service.RoleMenuService;
import com.github.tangyi.user.service.RoleService;
......@@ -19,7 +19,6 @@ import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
......@@ -120,7 +119,7 @@ public class RoleController extends BaseController {
* @date 2018/9/14 18:22
*/
@PutMapping
@PreAuthorize("hasAuthority('sys:role:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新角色信息", notes = "根据角色id更新角色的基本信息")
@ApiImplicitParam(name = "role", value = "角色实体role", required = true, dataType = "RoleVo")
@Log("修改角色")
......@@ -162,7 +161,7 @@ public class RoleController extends BaseController {
* @date 2018/9/14 18:23
*/
@PostMapping
@PreAuthorize("hasAuthority('sys:role:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建角色", notes = "创建角色")
@ApiImplicitParam(name = "role", value = "角色实体role", required = true, dataType = "RoleVo")
@Log("新增角色")
......@@ -180,7 +179,7 @@ public class RoleController extends BaseController {
* @date 2018/9/14 18:24
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:role:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除角色", notes = "根据ID删除角色")
@ApiImplicitParam(name = "id", value = "角色ID", required = true, paramType = "path")
@Log("删除角色")
......@@ -201,7 +200,7 @@ public class RoleController extends BaseController {
* @date 2018/12/4 10:00
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('sys:role:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "批量删除角色", notes = "根据角色id批量删除角色")
@ApiImplicitParam(name = "ids", value = "角色ID", dataType = "Long")
@Log("批量删除角色")
......
......@@ -8,6 +8,7 @@ import com.github.tangyi.common.core.utils.*;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.log.annotation.Log;
import com.github.tangyi.common.security.annotations.AdminTenantTeacherAuthorization;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.user.api.dto.UserDto;
import com.github.tangyi.user.api.dto.UserInfoDto;
......@@ -26,7 +27,6 @@ import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
......@@ -175,7 +175,7 @@ public class UserController extends BaseController {
* @date 2018/8/26 14:34
*/
@PostMapping
@PreAuthorize("hasAuthority('sys:user:add') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "创建用户", notes = "创建用户")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
@Log("新增用户")
......@@ -193,7 +193,7 @@ public class UserController extends BaseController {
* @date 2018/8/26 15:06
*/
@PutMapping("/{id:[a-zA-Z0-9,]+}")
@PreAuthorize("hasAuthority('sys:user:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "更新用户信息", notes = "根据用户id更新用户的基本信息、角色信息")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
@Log("修改用户")
......@@ -266,7 +266,7 @@ public class UserController extends BaseController {
* @date 2018/8/26 15:28
*/
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority('sys:user:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "删除用户", notes = "根据ID删除用户")
@ApiImplicitParam(name = "id", value = "用户ID", required = true, paramType = "path")
@Log("删除用户")
......@@ -291,7 +291,7 @@ public class UserController extends BaseController {
* @date 2018/11/26 22:11
*/
@PostMapping("export")
@PreAuthorize("hasAuthority('sys:user:export') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导出用户", notes = "根据用户id导出用户")
@ApiImplicitParam(name = "userVo", value = "用户信息", required = true, dataType = "UserVo")
@Log("导出用户")
......@@ -339,7 +339,7 @@ public class UserController extends BaseController {
* @date 2018/11/28 12:44
*/
@PostMapping("import")
@PreAuthorize("hasAuthority('sys:user:import') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "导入数据", notes = "导入数据")
@Log("导入用户")
public ResponseBean<Boolean> importUser(@ApiParam(value = "要上传的文件", required = true) MultipartFile file, HttpServletRequest request) {
......@@ -365,7 +365,7 @@ public class UserController extends BaseController {
* @date 2018/12/4 9:58
*/
@PostMapping("deleteAll")
@PreAuthorize("hasAuthority('sys:user:del') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "批量删除用户", notes = "根据用户id批量删除用户")
@ApiImplicitParam(name = "ids", value = "用户信息", dataType = "Long")
@Log("批量删除用户")
......@@ -460,7 +460,7 @@ public class UserController extends BaseController {
* @date 2019/6/7 12:00
*/
@PutMapping("/resetPassword")
@PreAuthorize("hasAuthority('sys:user:edit') or hasAnyRole('" + SecurityConstant.ROLE_ADMIN + "')")
@AdminTenantTeacherAuthorization
@ApiOperation(value = "重置密码", notes = "根据用户id重置密码")
@ApiImplicitParam(name = "userDto", value = "用户实体user", required = true, dataType = "UserDto")
@Log("重置密码")
......
......@@ -7,6 +7,7 @@ import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.SysUtil;
import com.github.tangyi.common.core.utils.TreeUtil;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.common.security.enums.Roles;
import com.github.tangyi.common.security.utils.SecurityUtil;
import com.github.tangyi.user.api.constant.MenuConstant;
import com.github.tangyi.user.api.dto.MenuDto;
......@@ -80,7 +81,7 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
List<Role> roleList = SecurityUtil.getCurrentAuthentication().getAuthorities().stream()
// 按角色过滤
.filter(authority -> authority.getAuthority() != null && authority.getAuthority()
.startsWith("role_")).map(authority -> {
.startsWith("ROLE_")).map(authority -> {
Role role = new Role();
role.setRoleCode(authority.getAuthority());
return role;
......@@ -88,7 +89,7 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
// 根据角色code批量查找菜单
List<Menu> tenantMenus = finMenuByRoleList(roleList, tenantCode);
// 组装数据
userMenus = mergeMenu(defaultMenus, tenantMenus);
userMenus = mergeMenu(getTenantMenus(defaultMenus), tenantMenus);
}
if (CollectionUtils.isNotEmpty(userMenus)) {
userMenus.stream()
......@@ -99,7 +100,8 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
// 去重
.distinct().forEach(menuDtoList::add);
// 排序、构建树形关系
return TreeUtil.buildTree(CollUtil.sort(menuDtoList, Comparator.comparingInt(MenuDto::getSort)), CommonConstant.ROOT);
return TreeUtil.buildTree(CollUtil.sort(menuDtoList, Comparator.comparingInt(MenuDto::getSort)),
CommonConstant.ROOT);
}
return Lists.newArrayList();
}
......@@ -298,7 +300,8 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
// 租户菜单
tenantMenus.forEach(tenantMenu -> {
Optional<Menu> exist = userMenus.stream()
.filter(userMenu -> userMenu.getName().equals(tenantMenu.getName()) && userMenu.getParentId().equals(tenantMenu.getParentId())).findFirst();
.filter(userMenu -> userMenu.getName().equals(tenantMenu.getName()) && userMenu.getParentId()
.equals(tenantMenu.getParentId())).findFirst();
if (!exist.isPresent()) {
userMenus.add(tenantMenu);
}
......@@ -349,6 +352,7 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
/**
* 根据租户code删除
*
* @param menu menu
* @return int
*/
......@@ -356,4 +360,25 @@ public class MenuService extends CrudService<MenuMapper, Menu> {
public int deleteByTenantCode(Menu menu) {
return this.dao.deleteByTenantCode(menu);
}
/**
* 获取租户权限的菜单
* @param defaultMenus defaultMenus
* @return List
*/
private List<Menu> getTenantMenus(List<Menu> defaultMenus) {
List<Menu> tenantMenus = new ArrayList<>();
if (CollectionUtils.isNotEmpty(defaultMenus)) {
defaultMenus.forEach(menu -> {
String permission = menu.getPermission();
// 过滤客户端管理、路由管理、系统监控菜单
if (!permission.equals(MenuConstant.MENU_CLIENT) && !permission.equals(MenuConstant.MENU_ROUTE)
&& !permission.equals(MenuConstant.MENU_TENANT) && !permission
.equals(MenuConstant.MENU_MONITOR)) {
tenantMenus.add(menu);
}
});
}
return tenantMenus;
}
}
......@@ -72,6 +72,7 @@
<okhttp.version>3.8.1</okhttp.version>
<aliyun.version>4.0.3</aliyun.version>
<weixin.version>3.4.0</weixin.version>
<jjwt.version>0.9.0</jjwt.version>
<!-- docker -->
<docker.maven.verion>1.4.3</docker.maven.verion>
......@@ -301,6 +302,13 @@
<artifactId>weixin-java-pay</artifactId>
<version>${weixin.version}</version>
</dependency>
<!-- jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>${jjwt.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment