Commit 131f4c8e by tangyi

优化

parent eeaaf0e2
Version v3.0.0 (2019-7-2)
--------------------------
改进:
* 优化swagger ui配置,增加租户标识请求头
* 完善手机号登录
Version v3.0.0 (2019-6-23) Version v3.0.0 (2019-6-23)
-------------------------- --------------------------
......
...@@ -126,7 +126,12 @@ public class CommonConstant { ...@@ -126,7 +126,12 @@ public class CommonConstant {
/** /**
* 保存code的前缀 * 保存code的前缀
*/ */
public static final String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY"; public static final String DEFAULT_CODE_KEY = "DEFAULT_CODE_KEY_";
/**
* 验证码长度
*/
public static final String CODE_SIZE = "4";
/** /**
* Bearer * Bearer
...@@ -138,5 +143,15 @@ public class CommonConstant { ...@@ -138,5 +143,15 @@ public class CommonConstant {
*/ */
public static final String GRANT_TYPE_PASSWORD = "password"; public static final String GRANT_TYPE_PASSWORD = "password";
/**
* 手机号类型
*/
public static final String GRANT_TYPE_MOBILE = "mobile";
/**
* 租户编号请求头
*/
public static final String TENANT_CODE_HEADER = "Tenant-Code";
} }
...@@ -22,4 +22,9 @@ public class ServiceConstant { ...@@ -22,4 +22,9 @@ public class ServiceConstant {
* 授权服务名称 * 授权服务名称
*/ */
public static final String AUTH_SERVICE = "auth-service"; public static final String AUTH_SERVICE = "auth-service";
/**
* 消息中心服务名称
*/
public static final String MSC_SERVICE = "msc-service";
} }
package com.github.tangyi.common.core.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 登录类型
*
* @author tangyi
* @date 2019/07/02 09:45
*/
@Getter
@AllArgsConstructor
public enum LoginType {
/**
* 账号密码登录
*/
PWD("PWD", "账号密码登录"),
/**
* 验证码登录
*/
SMS("SMS", "验证码登录"),
/**
* QQ登录
*/
QQ("QQ", "QQ登录"),
/**
* 微信登录
*/
WECHAT("WX", "微信登录");
/**
* 类型
*/
private String type;
/**
* 描述
*/
private String description;
}
...@@ -22,6 +22,11 @@ public class SecurityConstant { ...@@ -22,6 +22,11 @@ public class SecurityConstant {
public static final int DEFAULT_IMAGE_EXPIRE = 60; public static final int DEFAULT_IMAGE_EXPIRE = 60;
/** /**
* 默认短信验证码过期时间
*/
public static final int DEFAULT_SMS_EXPIRE = 5 * 60;
/**
* 正常状态 * 正常状态
*/ */
public static final String NORMAL = "0"; public static final String NORMAL = "0";
......
...@@ -33,7 +33,6 @@ public class TenantTokenFilter implements Filter { ...@@ -33,7 +33,6 @@ public class TenantTokenFilter implements Filter {
if (tenantCode == null) if (tenantCode == null)
tenantCode = SecurityConstant.DEFAULT_TENANT_CODE; tenantCode = SecurityConstant.DEFAULT_TENANT_CODE;
TenantContextHolder.setTenantCode(tenantCode); TenantContextHolder.setTenantCode(tenantCode);
log.info("租户code:{}", tenantCode);
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
TenantContextHolder.clear(); TenantContextHolder.clear();
} }
......
...@@ -82,6 +82,8 @@ ignore: ...@@ -82,6 +82,8 @@ ignore:
- /csrf - /csrf
- /actuator/** - /actuator/**
- /hystrix.sender - /hystrix.sender
- /v1/sms/**
- /v1/user/findUserBySocial/**
- /v1/user/findUserByUsername/** - /v1/user/findUserByUsername/**
- /v1/tenant/findTenantByTenantCode/** - /v1/tenant/findTenantByTenantCode/**
- /v1/user/checkExist/** - /v1/user/checkExist/**
......
...@@ -106,7 +106,9 @@ ignore: ...@@ -106,7 +106,9 @@ ignore:
- /csrf - /csrf
- /actuator/** - /actuator/**
- /hystrix.sender - /hystrix.sender
- /v1/sms/**
- /v1/user/findUserByUsername/** - /v1/user/findUserByUsername/**
- /v1/user/findUserBySocial/**
- /v1/tenant/findTenantByTenantCode/** - /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/** - /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu - /v1/menu/findAllMenu
......
...@@ -75,6 +75,7 @@ preview: ...@@ -75,6 +75,7 @@ preview:
- updateInfo - updateInfo
- attachment - attachment
- api/exam # 考试服务 - api/exam # 考试服务
- api/msc
# 开启网关token转换 # 开启网关token转换
gateway: gateway:
......
...@@ -2,9 +2,9 @@ server: ...@@ -2,9 +2,9 @@ server:
port: 8085 port: 8085
turbine: turbine:
appConfig: consul,auth-service,exam-service,user-service,gateway-service appConfig: consul,auth-service,exam-service,user-service,gateway-service,msc-service
aggregator: aggregator:
clusterConfig: CONSUL,AUTH-SERVICE,EXAM-SERVICE,USER-SERVICE,GATEWAY-SERVICE clusterConfig: CONSUL,AUTH-SERVICE,EXAM-SERVICE,USER-SERVICE,GATEWAY-SERVICE,MSC-SERVICE
spring: spring:
security: security:
......
...@@ -28,8 +28,8 @@ management: ...@@ -28,8 +28,8 @@ management:
show-details: ALWAYS show-details: ALWAYS
sms: sms:
appKey: appKey: test
appSecret: appSecret: test
regionId: cn-hangzhou regionId: cn-hangzhou
domain: dysmsapi.aliyuncs.com domain: dysmsapi.aliyuncs.com
...@@ -43,27 +43,19 @@ ignore: ...@@ -43,27 +43,19 @@ ignore:
- /csrf - /csrf
- /actuator/** - /actuator/**
- /hystrix.sender - /hystrix.sender
- /v1/user/findUserByUsername/** - /v1/sms/**
- /v1/tenant/findTenantByTenantCode/**
- /v1/user/checkExist/**
- /v1/user/updatePassword
- /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu
- /v1/code/**
- /v1/attachment/download
- /v1/log/**
- /authentication/**
- /v1/authentication/**
- /**/*.css - /**/*.css
- /**/*.js - /**/*.js
- /social
- /signin
- /signup
- /info - /info
- /health - /health
- /metrics/** - /metrics/**
- /loggers/** - /loggers/**
# 集群ID生成配置
cluster:
workId: ${CLUSTER_WORKID:1}
dataCenterId: ${CLUSTER_DATA_CENTER_ID:1}
logging: logging:
level: level:
root: info root: info
......
...@@ -130,7 +130,10 @@ ignore: ...@@ -130,7 +130,10 @@ ignore:
- /csrf - /csrf
- /actuator/** - /actuator/**
- /hystrix.sender - /hystrix.sender
- /v1/sms/**
- /v1/mobile/**
- /v1/user/findUserByUsername/** - /v1/user/findUserByUsername/**
- /v1/user/findUserBySocial/**
- /v1/tenant/findTenantByTenantCode/** - /v1/tenant/findTenantByTenantCode/**
- /v1/menu/findMenuByRole/** - /v1/menu/findMenuByRole/**
- /v1/menu/findAllMenu - /v1/menu/findAllMenu
......
...@@ -69,8 +69,8 @@ public class TokenRequestGlobalFilter implements GlobalFilter, Ordered { ...@@ -69,8 +69,8 @@ public class TokenRequestGlobalFilter implements GlobalFilter, Ordered {
if (HttpMethod.POST.matches(request.getMethodValue()) if (HttpMethod.POST.matches(request.getMethodValue())
&& StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) { && StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) {
String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE); String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE);
// 授权类型为密码模式 // 授权类型为密码、手机号模式
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || GatewayConstant.GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) { if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || CommonConstant.GRANT_TYPE_MOBILE.equals(grantType) || GatewayConstant.GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) {
// 装饰器 // 装饰器
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) { ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(exchange.getResponse()) {
@Override @Override
......
...@@ -2,6 +2,7 @@ package com.github.tangyi.gateway.filters; ...@@ -2,6 +2,7 @@ package com.github.tangyi.gateway.filters;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.enums.LoginType;
import com.github.tangyi.common.core.exceptions.InvalidValidateCodeException; import com.github.tangyi.common.core.exceptions.InvalidValidateCodeException;
import com.github.tangyi.common.core.exceptions.ValidateCodeExpiredException; import com.github.tangyi.common.core.exceptions.ValidateCodeExpiredException;
import com.github.tangyi.gateway.constants.GatewayConstant; import com.github.tangyi.gateway.constants.GatewayConstant;
...@@ -40,10 +41,10 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered { ...@@ -40,10 +41,10 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered {
if (HttpMethod.POST.matches(request.getMethodValue()) if (HttpMethod.POST.matches(request.getMethodValue())
&& StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) { && StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.OAUTH_TOKEN_URL, GatewayConstant.REGISTER, GatewayConstant.MOBILE_TOKEN_URL)) {
String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE); String grantType = request.getQueryParams().getFirst(GatewayConstant.GRANT_TYPE);
// 授权类型为密码模式、注册才校验验证码 // 授权类型为密码模式、手机号、注册才校验验证码
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) { if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType) || CommonConstant.GRANT_TYPE_MOBILE.equals(grantType) || StrUtil.containsAnyIgnoreCase(uri.getPath(), GatewayConstant.REGISTER)) {
// 校验验证码 // 校验验证码
checkCode(request); checkCode(request, getLoginType(grantType));
} }
} }
return chain.filter(exchange); return chain.filter(exchange);
...@@ -58,18 +59,23 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered { ...@@ -58,18 +59,23 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered {
* 校验验证码 * 校验验证码
* *
* @param serverHttpRequest serverHttpRequest * @param serverHttpRequest serverHttpRequest
* @param loginType loginType
* @throws InvalidValidateCodeException * @throws InvalidValidateCodeException
*/ */
private void checkCode(ServerHttpRequest serverHttpRequest) throws InvalidValidateCodeException { private void checkCode(ServerHttpRequest serverHttpRequest, LoginType loginType) throws InvalidValidateCodeException {
MultiValueMap<String, String> params = serverHttpRequest.getQueryParams(); MultiValueMap<String, String> params = serverHttpRequest.getQueryParams();
// 租户标识
String tenantCode = serverHttpRequest.getHeaders().getFirst("Tenant-Code");
// 验证码
String code = params.getFirst("code"); String code = params.getFirst("code");
if (StrUtil.isBlank(code)) if (StrUtil.isBlank(code))
throw new InvalidValidateCodeException("请输入验证码"); throw new InvalidValidateCodeException("请输入验证码.");
// 获取随机码 // 获取随机码
String randomStr = params.getFirst("randomStr"); String randomStr = params.getFirst("randomStr");
// 随机数为空,则获取手机号
if (StrUtil.isBlank(randomStr)) if (StrUtil.isBlank(randomStr))
randomStr = params.getFirst("mobile"); randomStr = params.getFirst("mobile");
String key = CommonConstant.DEFAULT_CODE_KEY + randomStr; String key = tenantCode + ":" + CommonConstant.DEFAULT_CODE_KEY + loginType.getType() + "@" + randomStr;
// 验证码过期 // 验证码过期
if (!redisTemplate.hasKey(key)) if (!redisTemplate.hasKey(key))
throw new ValidateCodeExpiredException(GatewayConstant.EXPIRED_ERROR); throw new ValidateCodeExpiredException(GatewayConstant.EXPIRED_ERROR);
...@@ -83,9 +89,22 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered { ...@@ -83,9 +89,22 @@ public class ValidateCodeFilter implements GlobalFilter, Ordered {
} }
if (!StrUtil.equals(saveCode, code)) { if (!StrUtil.equals(saveCode, code)) {
redisTemplate.delete(key); redisTemplate.delete(key);
throw new InvalidValidateCodeException("验证码错误,请重新输入"); throw new InvalidValidateCodeException("验证码错误.");
} }
redisTemplate.delete(key); redisTemplate.delete(key);
} }
/**
* 获取登录类型
*
* @param grantType grantType
* @return LoginType
*/
private LoginType getLoginType(String grantType) {
if (CommonConstant.GRANT_TYPE_PASSWORD.equals(grantType))
return LoginType.PWD;
if (CommonConstant.GRANT_TYPE_MOBILE.equals(grantType))
return LoginType.SMS;
return LoginType.PWD;
}
} }
...@@ -62,4 +62,9 @@ public class AccessToken implements Serializable { ...@@ -62,4 +62,9 @@ public class AccessToken implements Serializable {
* 租户标识 * 租户标识
*/ */
private String tenantCode; private String tenantCode;
/**
* 登录类型
*/
private String loginType;
} }
...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer {
@Bean @Bean
public Docket createRestApi() { public Docket createRestApi() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
List<Parameter> parameterList = new ArrayList<>(); List<Parameter> parameterList = new ArrayList<>();
tokenBuilder.name("Authorization") parameterList.add(authorizationParameter());
.defaultValue("去其他请求中获取heard中token参数") parameterList.add(tenantCodeParameter());
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
parameterList.add(tokenBuilder.build());
return new Docket(DocumentationType.SWAGGER_2) return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) .apiInfo(apiInfo())
.select() .select()
...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer {
.globalOperationParameters(parameterList); .globalOperationParameters(parameterList);
} }
/**
* Authorization 请求头
* @return Parameter
*/
private Parameter authorizationParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Authorization")
.defaultValue("bearer authorization参数")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
/**
* Tenant-Code 请求头
* @return Parameter
*/
private Parameter tenantCodeParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Tenant-Code")
.defaultValue("租户标识")
.description("租户标识")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder()
.title("Swagger API") .title("Swagger API")
......
package com.github.tangyi.auth.security; package com.github.tangyi.auth.security;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.enums.LoginType;
import com.github.tangyi.common.security.tenant.TenantContextHolder; import com.github.tangyi.common.security.tenant.TenantContextHolder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.OAuth2AccessToken;
...@@ -20,12 +21,17 @@ public class CustomTokenConverter extends JwtAccessTokenConverter { ...@@ -20,12 +21,17 @@ public class CustomTokenConverter extends JwtAccessTokenConverter {
@Override @Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
if (authentication.getOAuth2Request().getGrantType().equalsIgnoreCase(CommonConstant.GRANT_TYPE_PASSWORD)) {
final Map<String, Object> additionalInfo = new HashMap<>(); final Map<String, Object> additionalInfo = new HashMap<>();
String grantType = authentication.getOAuth2Request().getGrantType();
// 加入tenantCode // 加入tenantCode
additionalInfo.put("tenantCode", TenantContextHolder.getTenantCode()); additionalInfo.put("tenantCode", TenantContextHolder.getTenantCode());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); // 加入登录类型,用户名/手机号
if (grantType.equalsIgnoreCase(CommonConstant.GRANT_TYPE_PASSWORD)) {
additionalInfo.put("loginType", LoginType.PWD.getType());
} else if (grantType.equalsIgnoreCase(CommonConstant.GRANT_TYPE_MOBILE)) {
additionalInfo.put("loginType", LoginType.SMS.getType());
} }
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return super.enhance(accessToken, authentication); return super.enhance(accessToken, authentication);
} }
} }
...@@ -49,12 +49,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -49,12 +49,7 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
*/ */
@Override @Override
public UserDetails loadUserByUsernameAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException { public UserDetails loadUserByUsernameAndTenantCode(String username, String tenantCode) throws UsernameNotFoundException, TenantNotFoundException {
if (StringUtils.isBlank(tenantCode)) Tenant tenant = this.validateTenantCode(tenantCode);
throw new TenantNotFoundException("租户code不能为空.");
// 先获取租户信息
Tenant tenant = userServiceClient.findTenantByTenantCode(tenantCode);
if (tenant == null)
throw new TenantNotFoundException("租户不存在.");
UserVo userVo = userServiceClient.findUserByUsername(username, tenantCode); UserVo userVo = userServiceClient.findUserByUsername(username, tenantCode);
if (userVo == null) if (userVo == null)
throw new UsernameNotFoundException("用户名不存在."); throw new UsernameNotFoundException("用户名不存在.");
...@@ -72,16 +67,27 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService { ...@@ -72,16 +67,27 @@ public class CustomUserDetailsServiceImpl implements CustomUserDetailsService {
*/ */
@Override @Override
public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException { public UserDetails loadUserBySocialAndTenantCode(String social, String tenantCode) throws UsernameNotFoundException {
Tenant tenant = this.validateTenantCode(tenantCode);
UserVo userVo = userServiceClient.findUserBySocial(social, tenantCode);
if (userVo == null)
throw new UsernameNotFoundException("用户手机号未注册.");
return new CustomUserDetails(social, userVo.getPassword(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode());
}
/**
* 校验租户标识
*
* @param tenantCode tenantCode
* @return Tenant
*/
private Tenant validateTenantCode(String tenantCode) throws TenantNotFoundException {
if (StringUtils.isBlank(tenantCode)) if (StringUtils.isBlank(tenantCode))
throw new TenantNotFoundException("租户code不能为空."); throw new TenantNotFoundException("租户code不能为空.");
// 先获取租户信息 // 先获取租户信息
Tenant tenant = userServiceClient.findTenantByTenantCode(tenantCode); Tenant tenant = userServiceClient.findTenantByTenantCode(tenantCode);
if (tenant == null) if (tenant == null)
throw new TenantNotFoundException("租户不存在."); throw new TenantNotFoundException("租户不存在.");
UserVo userVo = userServiceClient.findUserBySocial(social, tenantCode); return tenant;
if (userVo == null)
throw new UsernameNotFoundException("用户名不存在.");
return new CustomUserDetails(social, userVo.getPassword(), CommonConstant.STATUS_NORMAL.equals(userVo.getStatus()), getAuthority(userVo), userVo.getTenantCode());
} }
/** /**
......
...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer {
@Bean @Bean
public Docket createRestApi() { public Docket createRestApi() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
List<Parameter> parameterList = new ArrayList<>(); List<Parameter> parameterList = new ArrayList<>();
tokenBuilder.name("Authorization") parameterList.add(authorizationParameter());
.defaultValue("去其他请求中获取heard中token参数") parameterList.add(tenantCodeParameter());
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
parameterList.add(tokenBuilder.build());
return new Docket(DocumentationType.SWAGGER_2) return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) .apiInfo(apiInfo())
.select() .select()
...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer {
.globalOperationParameters(parameterList); .globalOperationParameters(parameterList);
} }
/**
* Authorization 请求头
* @return Parameter
*/
private Parameter authorizationParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Authorization")
.defaultValue("bearer authorization参数")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
/**
* Tenant-Code 请求头
* @return Parameter
*/
private Parameter tenantCodeParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Tenant-Code")
.defaultValue("租户标识")
.description("租户标识")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder()
.title("Swagger API") .title("Swagger API")
......
...@@ -2,8 +2,20 @@ package com.github.tangyi.msc; ...@@ -2,8 +2,20 @@ package com.github.tangyi.msc;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
@SpringBootApplication @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@EnableDiscoveryClient
// 扫描api包里的FeignClient
@EnableFeignClients(basePackages = {"com.github.tangyi"})
@ComponentScan(basePackages = {"com.github.tangyi"})
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableCircuitBreaker
public class MscServiceApplication { public class MscServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
......
...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer {
@Bean @Bean
public Docket createRestApi() { public Docket createRestApi() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
List<Parameter> parameterList = new ArrayList<>(); List<Parameter> parameterList = new ArrayList<>();
tokenBuilder.name("Authorization") parameterList.add(authorizationParameter());
.defaultValue("去其他请求中获取heard中token参数") parameterList.add(tenantCodeParameter());
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
parameterList.add(tokenBuilder.build());
return new Docket(DocumentationType.SWAGGER_2) return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) .apiInfo(apiInfo())
.select() .select()
...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer {
.globalOperationParameters(parameterList); .globalOperationParameters(parameterList);
} }
/**
* Authorization 请求头
* @return Parameter
*/
private Parameter authorizationParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Authorization")
.defaultValue("bearer authorization参数")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
/**
* Tenant-Code 请求头
* @return Parameter
*/
private Parameter tenantCodeParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Tenant-Code")
.defaultValue("租户标识")
.description("租户标识")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder()
.title("Swagger API") .title("Swagger API")
......
# bootstrap.yml文件中的内容不能放到application.yml中,否则config部分无法被加载 # bootstrap.yml文件中的内容不能放到application.yml中,否则config部分无法被加载
# 因为config部分的配置先于application.yml被加载,而bootstrap.yml中的配置会先于application.yml加载 # 因为config部分的配置先于application.yml被加载,而bootstrap.yml中的配置会先于application.yml加载
spring: spring:
application: application:
name: msc-service name: msc-service
cloud: cloud:
# 使用consul作为注册中心 # 使用consul作为注册中心
consul: consul:
host: ${CONSUL_HOST:localhost} host: ${CONSUL_HOST:localhost}
port: ${CONSUL_PORT:8500} port: ${CONSUL_PORT:8500}
# 使用配置服务中心的配置信息
config: config:
fail-fast: true # 在某些情况下,如果服务无法连接到配置服务器,则可能希望启动服务失败,客户端将以异常停止 fail-fast: true # 在某些情况下,如果服务无法连接到配置服务器,则可能希望启动服务失败,客户端将以异常停止
retry: retry:
max-attempts: 5 # 配置客户端重试,默认行为是重试6次,初始退避间隔为1000ms,指数乘数为1.1 max-attempts: 5 # 配置客户端重试,默认行为是重试6次,初始退避间隔为1000ms,指数乘数为1.1
discovery: discovery:
# 默认false,设为true表示使用注册中心中的配置服务(服务发现)而不自己指定配置服务的地址(即uri) # 默认false,设为true表示使用注册中心中的配置服务(服务发现)而不自己指定配置服务的地址(即uri)
enabled: true enabled: true
# 指向配置中心在consul注册的服务名称(即:spring.application.name) # 指向配置中心在consul注册的服务名称(即:spring.application.name)
service-id: config-service service-id: config-service
\ No newline at end of file
...@@ -36,6 +36,12 @@ ...@@ -36,6 +36,12 @@
<artifactId>exam-api</artifactId> <artifactId>exam-api</artifactId>
</dependency> </dependency>
<!-- msc-api -->
<dependency>
<groupId>com.github.tangyi</groupId>
<artifactId>msc-api</artifactId>
</dependency>
<!-- web 服务 --> <!-- web 服务 -->
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
......
...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -34,15 +34,9 @@ public class SwaggerConfig implements WebMvcConfigurer {
@Bean @Bean
public Docket createRestApi() { public Docket createRestApi() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
List<Parameter> parameterList = new ArrayList<>(); List<Parameter> parameterList = new ArrayList<>();
tokenBuilder.name("Authorization") parameterList.add(authorizationParameter());
.defaultValue("去其他请求中获取heard中token参数") parameterList.add(tenantCodeParameter());
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
parameterList.add(tokenBuilder.build());
return new Docket(DocumentationType.SWAGGER_2) return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo()) .apiInfo(apiInfo())
.select() .select()
...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer { ...@@ -52,6 +46,36 @@ public class SwaggerConfig implements WebMvcConfigurer {
.globalOperationParameters(parameterList); .globalOperationParameters(parameterList);
} }
/**
* Authorization 请求头
* @return Parameter
*/
private Parameter authorizationParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Authorization")
.defaultValue("bearer authorization参数")
.description("令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
/**
* Tenant-Code 请求头
* @return Parameter
*/
private Parameter tenantCodeParameter() {
ParameterBuilder tokenBuilder = new ParameterBuilder();
tokenBuilder.name("Tenant-Code")
.defaultValue("租户标识")
.description("租户标识")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
return tokenBuilder.build();
}
private ApiInfo apiInfo() { private ApiInfo apiInfo() {
return new ApiInfoBuilder() return new ApiInfoBuilder()
.title("Swagger API") .title("Swagger API")
......
package com.github.tangyi.user.controller;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.user.service.MobileService;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
/**
* 手机管理Controller
*
* @author tangyi
* @date 2019/07/02 09:34
*/
@Slf4j
@AllArgsConstructor
@Api("手机管理")
@RestController
@RequestMapping("/v1/mobile")
public class MobileController extends BaseController {
private final MobileService mobileService;
/**
* 发送短信
*
* @param mobile mobile
* @param tenantCode tenantCode
* @return ResponseBean
* @author tangyi
* @date 2019/07/02 09:49:05
*/
@GetMapping("sendSms/{mobile}")
public ResponseBean<Boolean> sendSms(@PathVariable String mobile, @RequestHeader(SecurityConstant.TENANT_CODE_HEADER) String tenantCode) {
return mobileService.sendSms(mobile, tenantCode);
}
}
...@@ -31,6 +31,7 @@ import org.springframework.http.HttpHeaders; ...@@ -31,6 +31,7 @@ import org.springframework.http.HttpHeaders;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
...@@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletRequest; ...@@ -38,7 +39,6 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotBlank;
import java.security.Principal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
...@@ -89,9 +89,9 @@ public class UserController extends BaseController { ...@@ -89,9 +89,9 @@ public class UserController extends BaseController {
*/ */
@GetMapping("info") @GetMapping("info")
@ApiOperation(value = "获取用户信息", notes = "获取当前登录用户详细信息") @ApiOperation(value = "获取用户信息", notes = "获取当前登录用户详细信息")
public ResponseBean<UserInfoDto> user(Principal principal) { public ResponseBean<UserInfoDto> user(OAuth2Authentication authentication) {
UserVo userVo = new UserVo(); UserVo userVo = new UserVo();
userVo.setUsername(principal.getName()); userVo.setUsername(authentication.getName());
userVo.setTenantCode(SysUtil.getTenantCode()); userVo.setTenantCode(SysUtil.getTenantCode());
return new ResponseBean<>(userService.findUserInfo(userVo)); return new ResponseBean<>(userService.findUserInfo(userVo));
} }
...@@ -114,6 +114,23 @@ public class UserController extends BaseController { ...@@ -114,6 +114,23 @@ public class UserController extends BaseController {
} }
/** /**
* 根据用户手机号获取用户详细信息
*
* @param social social
* @param tenantCode tenantCode
* @return UserVo
*/
@GetMapping("/findUserBySocial/{social}")
@ApiOperation(value = "获取用户信息", notes = "根据用户手机号获取用户详细信息")
@ApiImplicitParams({
@ApiImplicitParam(name = "social", value = "用户手机号", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "tenantCode", value = "租户标识", required = true, dataType = "String"),
})
public UserVo findUserBySocial(@PathVariable String social, @RequestParam String tenantCode) {
return userService.selectUserVoBySocial(social, tenantCode);
}
/**
* 获取分页数据 * 获取分页数据
* *
* @param pageNum pageNum * @param pageNum pageNum
......
package com.github.tangyi.user.controller; package com.github.tangyi.user.controller;
import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.web.BaseController; import com.github.tangyi.common.core.web.BaseController;
import com.github.tangyi.user.service.UserService; import com.github.tangyi.user.service.UserService;
import com.google.code.kaptcha.Producer; import com.google.code.kaptcha.Producer;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
...@@ -44,18 +40,19 @@ public class ValidateCodeController extends BaseController { ...@@ -44,18 +40,19 @@ public class ValidateCodeController extends BaseController {
* @date 2018/9/14 20:13 * @date 2018/9/14 20:13
*/ */
@ApiOperation(value = "生成验证码", notes = "生成验证码") @ApiOperation(value = "生成验证码", notes = "生成验证码")
@ApiImplicitParam(name = "random", value = "随机数", required = true, dataType = "String", paramType = "path") @ApiImplicitParams({
@ApiImplicitParam(name = "random", value = "随机数", required = true, dataType = "String", paramType = "path"),
@ApiImplicitParam(name = "tenantCode", value = "租户标识", required = true, dataType = "String")
})
@GetMapping("/{random}") @GetMapping("/{random}")
public void produceCode(@PathVariable String random, HttpServletResponse response) throws Exception { public void produceCode(@PathVariable String random, @RequestParam String tenantCode, HttpServletResponse response) throws Exception {
if (StringUtils.isEmpty(random))
throw new CommonException("随机码不能为空!");
response.setHeader("Cache-Control", "no-store, no-cache"); response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg"); response.setContentType("image/jpeg");
// 生成文字验证码 // 生成文字验证码
String text = producer.createText(); String text = producer.createText();
// 生成图片验证码 // 生成图片验证码
BufferedImage image = producer.createImage(text); BufferedImage image = producer.createImage(text);
userService.saveImageCode(random, text); userService.saveImageCode(tenantCode, random, text);
ServletOutputStream out = response.getOutputStream(); ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "JPEG", out); ImageIO.write(image, "JPEG", out);
IOUtils.closeQuietly(out); IOUtils.closeQuietly(out);
......
...@@ -27,6 +27,15 @@ public interface UserMapper extends CrudMapper<User> { ...@@ -27,6 +27,15 @@ public interface UserMapper extends CrudMapper<User> {
UserVo selectUserVoByUsername(@Param("username") String username, @Param("tenantCode") String tenantCode); UserVo selectUserVoByUsername(@Param("username") String username, @Param("tenantCode") String tenantCode);
/** /**
* 通过用户手机号查询用户信息(含有角色信息)
*
* @param social 用户手机号
* @param tenantCode 租户标识
* @return userVo
*/
UserVo selectUserVoBySocial(@Param("social") String social, @Param("tenantCode") String tenantCode);
/**
* 查询用户数量 * 查询用户数量
* *
* @param userVo userVo * @param userVo userVo
......
package com.github.tangyi.user.service;
import cn.hutool.core.util.RandomUtil;
import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.enums.LoginType;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.core.vo.UserVo;
import com.github.tangyi.common.security.constant.SecurityConstant;
import com.github.tangyi.msc.api.constant.SmsConstant;
import com.github.tangyi.msc.api.dto.SmsDto;
import com.github.tangyi.msc.api.feign.MscServiceClient;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/**
* 手机管理Service
*
* @author tangyi
* @date 2019/07/02 09:35
*/
@Slf4j
@AllArgsConstructor
@Service
public class MobileService {
private final UserService userService;
private final RedisTemplate redisTemplate;
private final MscServiceClient mscServiceClient;
/**
* 发送短信
*
* @param mobile mobile
* @param tenantCode tenantCode
* @return ResponseBean
* @author tangyi
* @date 2019/07/02 09:36:52
*/
public ResponseBean<Boolean> sendSms(String mobile, String tenantCode) {
UserVo userVo = userService.selectUserVoBySocial(mobile, tenantCode);
if (userVo == null) {
log.info("手机号未注册:{}", mobile);
return new ResponseBean<>(Boolean.FALSE, "手机号未注册.");
}
Object codeObj = redisTemplate.opsForValue().get(CommonConstant.DEFAULT_CODE_KEY + mobile);
if (codeObj != null) {
log.info("手机号验证码未过期:{},{}", mobile, codeObj);
return new ResponseBean<>(Boolean.FALSE, "手机号未注册.");
}
String code = RandomUtil.randomNumbers(Integer.parseInt(CommonConstant.CODE_SIZE));
log.debug("手机号生成验证码成功:{},{}", mobile, code);
redisTemplate.opsForValue().set(tenantCode + ":" + CommonConstant.DEFAULT_CODE_KEY + LoginType.SMS.getType() + "@" + mobile
, code, SecurityConstant.DEFAULT_SMS_EXPIRE, TimeUnit.SECONDS);
// 调用消息中心服务,发送短信验证码
SmsDto smsDto = new SmsDto();
smsDto.setReceiver(mobile);
smsDto.setContent(String.format(SmsConstant.SMS_TEMPLATE, code));
mscServiceClient.sendSms(smsDto);
return new ResponseBean<>(Boolean.TRUE, code);
}
}
package com.github.tangyi.user.service; package com.github.tangyi.user.service;
import com.github.tangyi.common.core.constant.CommonConstant; import com.github.tangyi.common.core.constant.CommonConstant;
import com.github.tangyi.common.core.enums.LoginType;
import com.github.tangyi.common.core.exceptions.CommonException; import com.github.tangyi.common.core.exceptions.CommonException;
import com.github.tangyi.common.core.service.CrudService; import com.github.tangyi.common.core.service.CrudService;
import com.github.tangyi.common.core.utils.IdGen; import com.github.tangyi.common.core.utils.IdGen;
...@@ -90,11 +91,14 @@ public class UserService extends CrudService<UserMapper, User> { ...@@ -90,11 +91,14 @@ public class UserService extends CrudService<UserMapper, User> {
* @date 2018/9/11 23:44 * @date 2018/9/11 23:44
*/ */
public UserInfoDto findUserInfo(UserVo userVo) { public UserInfoDto findUserInfo(UserVo userVo) {
String tenantCode = userVo.getTenantCode(); String tenantCode = userVo.getTenantCode(), username = userVo.getUsername();
// 根据用户名查询用户信息 // 根据用户名查询用户信息
userVo = userMapper.selectUserVoByUsername(userVo.getUsername(), tenantCode); userVo = userMapper.selectUserVoByUsername(username, tenantCode);
if (userVo == null)
userVo = userMapper.selectUserVoBySocial(username, tenantCode);
if (userVo == null)
throw new CommonException("查询用户信息失败.");
UserInfoDto user = new UserInfoDto(); UserInfoDto user = new UserInfoDto();
if (userVo != null) {
BeanUtils.copyProperties(userVo, user); BeanUtils.copyProperties(userVo, user);
// 用户角色 // 用户角色
List<String> roles = new ArrayList<>(); List<String> roles = new ArrayList<>();
...@@ -123,7 +127,6 @@ public class UserService extends CrudService<UserMapper, User> { ...@@ -123,7 +127,6 @@ public class UserService extends CrudService<UserMapper, User> {
this.initUserAvatar(user, userVo); this.initUserAvatar(user, userVo);
user.setRoles(roles.toArray(new String[0])); user.setRoles(roles.toArray(new String[0]));
user.setPermissions(permissions.toArray(new String[0])); user.setPermissions(permissions.toArray(new String[0]));
}
return user; return user;
} }
...@@ -215,15 +218,28 @@ public class UserService extends CrudService<UserMapper, User> { ...@@ -215,15 +218,28 @@ public class UserService extends CrudService<UserMapper, User> {
} }
/** /**
* 根据用户手机号查找
*
* @param social social
* @param tenantCode tenantCode
* @return UserVo
*/
@Cacheable(value = "user", key = "#social")
public UserVo selectUserVoBySocial(String social, String tenantCode) {
return userMapper.selectUserVoBySocial(social, tenantCode);
}
/**
* 保存验证码 * 保存验证码
* *
* @param tenantCode tenantCode
* @param random random * @param random random
* @param imageCode imageCode * @param imageCode imageCode
* @author tangyi * @author tangyi
* @date 2018/9/14 20:12 * @date 2018/9/14 20:12
*/ */
public void saveImageCode(String random, String imageCode) { public void saveImageCode(String tenantCode, String random, String imageCode) {
redisTemplate.opsForValue().set(CommonConstant.DEFAULT_CODE_KEY + random, imageCode, SecurityConstant.DEFAULT_IMAGE_EXPIRE, TimeUnit.SECONDS); redisTemplate.opsForValue().set(tenantCode + ":" + CommonConstant.DEFAULT_CODE_KEY + LoginType.PWD.getType() + "@" + random, imageCode, SecurityConstant.DEFAULT_IMAGE_EXPIRE, TimeUnit.SECONDS);
} }
/** /**
......
...@@ -152,6 +152,11 @@ ...@@ -152,6 +152,11 @@
WHERE `user`.username = #{username} and `user`.tenant_code = #{tenantCode} and `user`.del_flag = 0 WHERE `user`.username = #{username} and `user`.tenant_code = #{tenantCode} and `user`.del_flag = 0
</select> </select>
<select id="selectUserVoBySocial" resultMap="userVoResultMap">
<include refid="selectUserVo"/>
WHERE `user`.phone = #{social} and `user`.tenant_code = #{tenantCode} and `user`.del_flag = 0
</select>
<select id="get" resultMap="userResultMap"> <select id="get" resultMap="userResultMap">
SELECT SELECT
<include refid="userColumns"/> <include refid="userColumns"/>
......
...@@ -5,4 +5,9 @@ package com.github.tangyi.msc.api.constant; ...@@ -5,4 +5,9 @@ package com.github.tangyi.msc.api.constant;
* @date 2019/6/22 13:18 * @date 2019/6/22 13:18
*/ */
public class SmsConstant { public class SmsConstant {
/**
* 短信模板
*/
public static final String SMS_TEMPLATE = "验证码:%s,本验证码有效时间5分钟,请勿告知其他人。";
} }
package com.github.tangyi.msc.api.feign;
import com.github.tangyi.common.core.constant.ServiceConstant;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.common.feign.config.CustomFeignConfig;
import com.github.tangyi.msc.api.dto.SmsDto;
import com.github.tangyi.msc.api.feign.factory.MscServiceClientFallbackFactory;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* 消息中心服务
*
* @author tangyi
* @date 2019/07/02 16:04
*/
@FeignClient(value = ServiceConstant.MSC_SERVICE, configuration = CustomFeignConfig.class, fallbackFactory = MscServiceClientFallbackFactory.class)
public interface MscServiceClient {
/**
* 发送短信
*
* @param smsDto smsDto
* @return ResponseBean
* @author tangyi
* @date 2019/07/02 16:07:27
*/
@PostMapping("/v1/sms/sendSms")
ResponseBean<?> sendSms(@RequestBody SmsDto smsDto);
}
package com.github.tangyi.msc.api.feign.factory;
import com.github.tangyi.msc.api.feign.MscServiceClient;
import com.github.tangyi.msc.api.feign.fallback.MscServiceClientFallbackImpl;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
/**
* 消息中心服务断路器工厂
*
* @author tangyi
* @date 2019/07/02 16:08
*/
@Component
public class MscServiceClientFallbackFactory implements FallbackFactory<MscServiceClient> {
@Override
public MscServiceClient create(Throwable throwable) {
MscServiceClientFallbackImpl mscServiceClientFallback = new MscServiceClientFallbackImpl();
mscServiceClientFallback.setThrowable(throwable);
return mscServiceClientFallback;
}
}
package com.github.tangyi.msc.api.feign.fallback;
import com.github.tangyi.common.core.model.ResponseBean;
import com.github.tangyi.msc.api.dto.SmsDto;
import com.github.tangyi.msc.api.feign.MscServiceClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 消息中心服务断路器
*
* @author tangyi
* @date 2019/07/02 16:09
*/
@Slf4j
@Component
public class MscServiceClientFallbackImpl implements MscServiceClient {
private Throwable throwable;
@Override
public ResponseBean<?> sendSms(SmsDto smsDto) {
log.error("feign 发送短信失败:{}, {}, {}", smsDto.getReceiver(), smsDto.getContent(), throwable);
return null;
}
public Throwable getThrowable() {
return throwable;
}
public void setThrowable(Throwable throwable) {
this.throwable = throwable;
}
}
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