feat: 新增钉钉机器人异常告警

11111
winter 1 year ago
parent 46fa7bf45e
commit f0a9ba9243

@ -1,8 +1,10 @@
# TeamModel extension # TeamModel extension
> SpringBoot base version of TeamModel extension > SpringBoot base version of TeamModel extension
> >
> **注意**: 所有复盘输出均已脱敏,不包含任何业务,密码等关键信息 > **注意**: 所有复盘输出均已脱敏,不包含任何业务,密码等关键信息
## 迁移目录: ## 迁移目录:
- Azure OIDC(SSO) 迁移 - Azure OIDC(SSO) 迁移
- id-token(jwt) 验证迁移 (出现语言框架之间的签名算法规范问题,解决见: [输出复盘](https://juejin.cn/post/7300036605099163702)) - id-token(jwt) 验证迁移 (出现语言框架之间的签名算法规范问题,解决见: [输出复盘](https://juejin.cn/post/7300036605099163702))
- 钉钉告警: 异常通知
- 异常文件记录

@ -0,0 +1,37 @@
package cn.teammodel.common;
public enum ErrorCode {
SUCCESS(0, "ok"),
PARAMS_ERROR(40000, "请求参数错误"),
NOT_LOGIN_ERROR(40100, "未登录"),
NO_AUTH_ERROR(40101, "无权限"),
NOT_FOUND_ERROR(40400, "请求数据不存在"),
FORBIDDEN_ERROR(40300, "禁止访问"),
SYSTEM_ERROR(50000, "系统内部异常"),
OPERATION_ERROR(50001, "操作失败");
/**
*
*/
private final int code;
/**
*
*/
private final String message;
ErrorCode(int code, String message) {
this.code = code;
this.message = message;
}
public int getCode() {
return code;
}
public String getMessage() {
return message;
}
}

@ -1,37 +1,48 @@
package cn.teammodel.common; package cn.teammodel.common;
public class R { import lombok.Data;
private Integer code;
private String message; import java.io.Serializable;
private String data;
@Data
public R(Integer code, String message, String data) { public class R<T> implements Serializable {
this.code = code;
this.message = message; private int code;
this.data = data;
} private T data;
public Integer getCode() { private String message;
return code;
} public R(int code, T data, String message) {
this.code = code;
public void setCode(Integer code) { this.data = data;
this.code = code; this.message = message;
} }
public String getMessage() { public R(int code, T data) {
return message; this(code, data, "");
} }
public void setMessage(String message) { public R(ErrorCode errorCode) {
this.message = message; this(errorCode.getCode(), null, errorCode.getMessage());
} }
public R(ErrorCode errorCode, T data) {
public String getData() { this(errorCode.getCode(), data, errorCode.getMessage());
return data; }
}
public static <T> R<T> success(T data) {
public void setData(String data) { return new R<>(ErrorCode.SUCCESS, data);
this.data = data; }
}
} public static <T> R<T> error(String msg) {
return new R<>(ErrorCode.SYSTEM_ERROR);
}
public static <T> R<T> error(Integer code, String msg) {
return new R<>(code, null, msg);
}
public static <T> R<T> error(ErrorCode errorCode) {
return new R<>(errorCode);
}
}

@ -0,0 +1,110 @@
package cn.teammodel.config.exception;
import cn.teammodel.common.R;
import cn.teammodel.manager.NotificationService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
*
*
* @author ruoyi
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@Resource
private NotificationService notificationService;
/**
*
*/
@ExceptionHandler(ServiceException.class)
public R<?> handleServiceException(ServiceException e, HttpServletRequest request)
{
log.error(e.getMessage(), e);
Integer code = e.getCode();
return ObjectUtils.isNotEmpty(code) ? R.error(code, e.getMessage()) : R.error(e.getMessage());
}
/**
*
*/
@ExceptionHandler(MissingPathVariableException.class)
public R<?> handleMissingPathVariableException(MissingPathVariableException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求路径中缺少必需的路径变量'{}',发生系统异常.", requestURI, e);
return R.error(String.format("请求路径中缺少必需的路径变量[%s]", e.getVariableName()));
}
/**
*
*/
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
public R<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求参数类型不匹配'{}',发生系统异常.", requestURI, e);
return R.error(String.format("请求参数类型不匹配,参数[%s]要求类型为:'%s',但输入值为:'%s'", e.getName(), e.getRequiredType().getName(), e.getValue()));
}
/**
*
*/
@ExceptionHandler(RuntimeException.class)
public R<?> handleRuntimeException(RuntimeException e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生未知异常.", requestURI, e);
notificationService.send("RuntimeException 告警: " + e.getMessage());
return R.error(e.getMessage());
}
/**
*
*/
@ExceptionHandler(Exception.class)
public R<?> handleException(Exception e, HttpServletRequest request)
{
String requestURI = request.getRequestURI();
log.error("请求地址'{}',发生系统异常.", requestURI, e);
notificationService.send("Exception 告警: " + e.getMessage());
return R.error(e.getMessage());
}
/**
*
*/
@ExceptionHandler(BindException.class)
public R<?> handleBindException(BindException e)
{
log.error(e.getMessage(), e);
String message = e.getAllErrors().get(0).getDefaultMessage();
return R.error(message);
}
/**
*
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e)
{
log.error(e.getMessage(), e);
String message = Objects.requireNonNull(e.getBindingResult().getFieldError()).getDefaultMessage();
return R.error(message);
}
}

@ -1,72 +1,72 @@
package cn.teammodel.config.exception; package cn.teammodel.config.exception;
/** /**
* *
* *
* @author winter * @author winter
*/ */
public final class ServiceException extends RuntimeException public final class ServiceException extends RuntimeException
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* *
*/ */
private Integer code; private Integer code;
/** /**
* *
*/ */
private String message; private String message;
/** /**
* *
*/ */
private String detailMessage; private String detailMessage;
/** /**
* *
*/ */
public ServiceException() public ServiceException()
{ {
} }
public ServiceException(String message) public ServiceException(String message)
{ {
this.message = message; this.message = message;
} }
public ServiceException(String message, Integer code) public ServiceException(String message, Integer code)
{ {
this.message = message; this.message = message;
this.code = code; this.code = code;
} }
public String getDetailMessage() public String getDetailMessage()
{ {
return detailMessage; return detailMessage;
} }
@Override @Override
public String getMessage() public String getMessage()
{ {
return message; return message;
} }
public Integer getCode() public Integer getCode()
{ {
return code; return code;
} }
public ServiceException setMessage(String message) public ServiceException setMessage(String message)
{ {
this.message = message; this.message = message;
return this; return this;
} }
public ServiceException setDetailMessage(String detailMessage) public ServiceException setDetailMessage(String detailMessage)
{ {
this.detailMessage = detailMessage; this.detailMessage = detailMessage;
return this; return this;
} }
} }

@ -1,20 +1,20 @@
package cn.teammodel.controller; package cn.teammodel.controller;
import cn.teammodel.common.R; import cn.teammodel.common.R;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@RestController @RestController
@RequestMapping("/") @RequestMapping("/")
public class HelloController { public class HelloController {
@GetMapping("hello") @GetMapping("hello")
@PreAuthorize("@ss.hasRole('admin')") @PreAuthorize("@ss.hasRole('admin')")
public R hello() { public R hello() {
System.out.println(SecurityContextHolder.getContext().getAuthentication()); System.out.println(SecurityContextHolder.getContext().getAuthentication());
return new R(200, "success","hello world"); return new R(200, "success","hello world");
} }
} }

@ -0,0 +1,44 @@
package cn.teammodel.manager;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
*
* @author winter
* @create 2023-11-14 10:11
*/
@Component
@Slf4j
public class DingAlertNotifier implements NotificationService{
private final DingTalkClient client;
@Autowired
public DingAlertNotifier(@Value("${ding.server-url}") String dingServerUrl) {
this.client = new DefaultDingTalkClient(dingServerUrl);
}
@Override
public void send(String message) {
OapiRobotSendRequest request = new OapiRobotSendRequest();
// 文本消息
request.setMsgtype("text");
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent(message);
request.setText(text);
// at 管理员提醒异常
OapiRobotSendRequest.At atAll = new OapiRobotSendRequest.At();
atAll.setIsAtAll(true);
request.setAt(atAll);
try {
client.execute(request);
} catch (ApiException e) {
log.error("钉钉 robot 推送消息渠道异常: {}", e.getMessage());
}
}
}

@ -0,0 +1,16 @@
package cn.teammodel.manager;
/**
*
* @author winter
* @create 2023-11-14 10:08
*/
public interface NotificationService {
/**
*
* @author: winter
* @date: 2023/11/14 10:09
*/
void send(String message);
}

@ -1,50 +1,50 @@
package cn.teammodel.model.entity; package cn.teammodel.model.entity;
import lombok.Data; import lombok.Data;
import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection; import java.util.Collection;
/** /**
* @author winter * @author winter
* @create 2023-11-09 15:28 * @create 2023-11-09 15:28
*/ */
@Data @Data
public class TmdUserDetail implements UserDetails { public class TmdUserDetail implements UserDetails {
private User user; private User user;
@Override @Override
public Collection<? extends GrantedAuthority> getAuthorities() { public Collection<? extends GrantedAuthority> getAuthorities() {
return null; return null;
} }
@Override @Override
public String getPassword() { public String getPassword() {
return null; return null;
} }
@Override @Override
public String getUsername() { public String getUsername() {
return null; return null;
} }
@Override @Override
public boolean isAccountNonExpired() { public boolean isAccountNonExpired() {
return false; return false;
} }
@Override @Override
public boolean isAccountNonLocked() { public boolean isAccountNonLocked() {
return false; return false;
} }
@Override @Override
public boolean isCredentialsNonExpired() { public boolean isCredentialsNonExpired() {
return false; return false;
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return true; return true;
} }
} }

@ -1,24 +1,24 @@
package cn.teammodel.model.entity; package cn.teammodel.model.entity;
import lombok.Data; import lombok.Data;
import lombok.ToString; import lombok.ToString;
import java.util.Set; import java.util.Set;
/** /**
* @author winter * @author winter
* @create 2023-11-09 15:43 * @create 2023-11-09 15:43
*/ */
@Data @Data
@ToString @ToString
public class User { public class User {
private String id; private String id;
private String name; private String name;
private String picture; private String picture;
private String standard; private String standard;
private String scope; private String scope;
private String website; private String website;
private String area; private String area;
private Set<String> roles; private Set<String> roles;
private Set<String> permissions; private Set<String> permissions;
} }

@ -1,67 +1,63 @@
package cn.teammodel.security; package cn.teammodel.security;
import cn.teammodel.security.filter.AuthInnerTokenFilter; import cn.teammodel.security.filter.AuthInnerTokenFilter;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; 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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer; import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter;
import org.springframework.security.web.SecurityFilterChain; import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController; @Configuration
@EnableWebSecurity
import javax.annotation.Resource; @EnableMethodSecurity
public class SecurityConfiguration {
@Configuration @Resource
@EnableWebSecurity private AuthInnerTokenFilter authInnerTokenFilter;
@EnableMethodSecurity
public class SecurityConfiguration { @Bean
@Resource public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
private AuthInnerTokenFilter authInnerTokenFilter; http
// CSRF禁用因为不使用session
@Bean .csrf().disable()
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // 禁用HTTP响应标头
http .headers().cacheControl().disable()
// CSRF禁用因为不使用session .and()
.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 禁用HTTP响应标头 .and()
.headers().cacheControl().disable() .authorizeRequests(authorizeRequests ->
.and() authorizeRequests
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .antMatchers("/public/**").permitAll()
.and() .anyRequest().authenticated()
.authorizeRequests(authorizeRequests -> )
authorizeRequests .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // 启用 OIDC jwt filter
.antMatchers("/public/**").permitAll() .addFilterAfter(authInnerTokenFilter, BearerTokenAuthenticationFilter.class); // 添加 x-auth-authToken filter
.anyRequest().authenticated() // todo 失败处理器
)
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt) // 启用 OIDC jwt filter return http.build();
.addFilterAfter(authInnerTokenFilter, BearerTokenAuthenticationFilter.class); // 添加 x-auth-authToken filter }
// todo 失败处理器
/**
return http.build(); * OIDC: jwt claim roles authorities
} *
* @author: winter
/** * @date: 2023/11/8 17:13
* OIDC: jwt claim roles authorities */
* @Bean
* @author: winter public JwtAuthenticationConverter jwtAuthenticationConverter() {
* @date: 2023/11/8 17:13 JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
*/ grantedAuthoritiesConverter.setAuthoritiesClaimName("roles");
@Bean grantedAuthoritiesConverter.setAuthorityPrefix("");
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("roles"); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
grantedAuthoritiesConverter.setAuthorityPrefix(""); return jwtAuthenticationConverter;
}
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); }
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
}

@ -1,55 +1,53 @@
package cn.teammodel.security.filter; package cn.teammodel.security.filter;
import cn.teammodel.config.exception.ServiceException; import cn.teammodel.model.entity.TmdUserDetail;
import cn.teammodel.model.entity.TmdUserDetail; import cn.teammodel.security.utils.JwtTokenUtil;
import cn.teammodel.security.utils.JwtTokenUtil; import lombok.extern.slf4j.Slf4j;
import cn.teammodel.security.utils.SecurityUtils; import org.springframework.beans.factory.annotation.Autowired;
import lombok.extern.slf4j.Slf4j; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.GrantedAuthority; import org.springframework.stereotype.Component;
import org.springframework.security.core.context.SecurityContext; import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component; import javax.servlet.FilterChain;
import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.FilterChain; import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletException; import java.io.IOException;
import javax.servlet.http.HttpServletRequest; import java.nio.file.AccessDeniedException;
import javax.servlet.http.HttpServletResponse; import java.util.Collection;
import java.io.IOException;
import java.util.Collection; /**
* x-auth-authToken filter
/** * @author winter
* x-auth-authToken filter * @create 2023-11-09 10:43
* @author winter */
* @create 2023-11-09 10:43 @Component
*/ @Slf4j
@Component public class AuthInnerTokenFilter extends OncePerRequestFilter {
@Slf4j
public class AuthInnerTokenFilter extends OncePerRequestFilter { @Autowired
JwtTokenUtil jwtTokenUtil;
@Autowired
JwtTokenUtil jwtTokenUtil; @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
@Override SecurityContext context = SecurityContextHolder.getContext();
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { Authentication authentication = context.getAuthentication();
SecurityContext context = SecurityContextHolder.getContext(); // 进入此过滤器说明 OIDC 认证成功,则验证 authToken
Authentication authentication = context.getAuthentication(); // 验证 authToken 合法
// 进入此过滤器说明 OIDC 认证成功,则验证 authToken TmdUserDetail tmdUserDetail = jwtTokenUtil.getValidUserDetail(request);
// 验证 authToken 合法 if (tmdUserDetail == null) {
TmdUserDetail tmdUserDetail = jwtTokenUtil.getValidUserDetail(request); log.error("authToken authentication failed");
if (tmdUserDetail == null) { throw new AccessDeniedException("无权限");
log.error("authToken authentication failed"); }
throw new ServiceException("无权限"); System.out.println(tmdUserDetail.getUser());
} // 组装 authToken 的 jwt 进 authentication
System.out.println(tmdUserDetail.getUser()); Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
// 组装 authToken 的 jwt 进 authentication UsernamePasswordAuthenticationToken finalAuthentication = new UsernamePasswordAuthenticationToken(tmdUserDetail, null, authorities);
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities(); context.setAuthentication(finalAuthentication);
UsernamePasswordAuthenticationToken finalAuthentication = new UsernamePasswordAuthenticationToken(tmdUserDetail, null, authorities); filterChain.doFilter(request, response);
context.setAuthentication(finalAuthentication); }
filterChain.doFilter(request, response); }
}
}

@ -1,174 +1,174 @@
package cn.teammodel.security.service; package cn.teammodel.security.service;
import java.util.Set; import java.util.Set;
import cn.teammodel.model.entity.User; import cn.teammodel.model.entity.User;
import cn.teammodel.security.utils.SecurityUtils; import cn.teammodel.security.utils.SecurityUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
* ssSpringSecurity <br/> * ssSpringSecurity <br/>
* <p> * <p>
* 1. IES : hasAuth <br/> * 1. IES : hasAuth <br/>
* 2. role (authToken ): hasRole <br/> * 2. role (authToken ): hasRole <br/>
* 3. permission (authToken ): hasPermi <br/> * 3. permission (authToken ): hasPermi <br/>
* </p> * </p>
* @author ruoyi * @author ruoyi
*/ */
@Service("ss") @Service("ss")
public class PermissionService public class PermissionService
{ {
/** 所有权限标识 */ /** 所有权限标识 */
private static final String ALL_PERMISSION = "*:*:*"; private static final String ALL_PERMISSION = "*:*:*";
/** 管理员角色权限标识 */ /** 管理员角色权限标识 */
private static final String SUPER_ADMIN = "admin"; private static final String SUPER_ADMIN = "admin";
private static final String ROLE_DELIMETER = ","; private static final String ROLE_DELIMETER = ",";
private static final String PERMISSION_DELIMETER = ","; private static final String PERMISSION_DELIMETER = ",";
/** /**
* access_token auth * access_token auth
* @param auth: * @param auth:
* @return: boolean * @return: boolean
* @author: winter * @author: winter
* @date: 2023/11/10 10:05 * @date: 2023/11/10 10:05
* @description: * @description:
*/ */
public boolean hasAuth(String auth) public boolean hasAuth(String auth)
{ {
if (StringUtils.isEmpty(auth)) if (StringUtils.isEmpty(auth))
{ {
return false; return false;
} }
Authentication authentication = SecurityUtils.getAuthentication(); Authentication authentication = SecurityUtils.getAuthentication();
if (authentication == null || CollectionUtils.isEmpty(authentication.getAuthorities())) if (authentication == null || CollectionUtils.isEmpty(authentication.getAuthorities()))
{ {
return false; return false;
} }
return authentication.getAuthorities().contains(auth); return authentication.getAuthorities().contains(auth);
} }
/** /**
* *
* *
* @param permission * @param permission
* @return * @return
*/ */
public boolean hasPermi(String permission) public boolean hasPermi(String permission)
{ {
if (StringUtils.isEmpty(permission)) if (StringUtils.isEmpty(permission))
{ {
return false; return false;
} }
User loginUser = SecurityUtils.getLoginUser(); User loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions())) if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions()))
{ {
return false; return false;
} }
return loginUser.getPermissions().contains(permission); return loginUser.getPermissions().contains(permission);
} }
/** /**
* hasPermi * hasPermi
* *
* @param permission * @param permission
* @return * @return
*/ */
public boolean lacksPermi(String permission) public boolean lacksPermi(String permission)
{ {
return !hasPermi(permission); return !hasPermi(permission);
} }
/** /**
* *
* *
* @param permissions PERMISSION_DELIMETER * @param permissions PERMISSION_DELIMETER
* @return * @return
*/ */
public boolean hasAnyPermi(String permissions) public boolean hasAnyPermi(String permissions)
{ {
if (StringUtils.isEmpty(permissions)) if (StringUtils.isEmpty(permissions))
{ {
return false; return false;
} }
User loginUser = SecurityUtils.getLoginUser(); User loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions())) if (loginUser == null || CollectionUtils.isEmpty(loginUser.getPermissions()))
{ {
return false; return false;
} }
Set<String> authorities = loginUser.getPermissions(); Set<String> authorities = loginUser.getPermissions();
for (String permission : permissions.split(PERMISSION_DELIMETER)) for (String permission : permissions.split(PERMISSION_DELIMETER))
{ {
if (permission != null && authorities.contains(permission)) if (permission != null && authorities.contains(permission))
{ {
return true; return true;
} }
} }
return false; return false;
} }
/** /**
* *
* *
* @param role * @param role
* @return * @return
*/ */
public boolean hasRole(String role) public boolean hasRole(String role)
{ {
if (StringUtils.isEmpty(role)) { if (StringUtils.isEmpty(role)) {
return false; return false;
} }
User loginUser = SecurityUtils.getLoginUser(); User loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getRoles())) if (loginUser == null || CollectionUtils.isEmpty(loginUser.getRoles()))
{ {
return false; return false;
} }
return loginUser.getRoles().contains(role); return loginUser.getRoles().contains(role);
} }
/** /**
* isRole * isRole
* *
* @param role * @param role
* @return * @return
*/ */
public boolean lacksRole(String role) public boolean lacksRole(String role)
{ {
return !hasRole(role); return !hasRole(role);
} }
/** /**
* *
* *
* @param roles ROLE_NAMES_DELIMETER * @param roles ROLE_NAMES_DELIMETER
* @return * @return
*/ */
public boolean hasAnyRoles(String roles) public boolean hasAnyRoles(String roles)
{ {
if (StringUtils.isEmpty(roles)) if (StringUtils.isEmpty(roles))
{ {
return false; return false;
} }
User loginUser = SecurityUtils.getLoginUser(); User loginUser = SecurityUtils.getLoginUser();
if (loginUser == null || CollectionUtils.isEmpty(loginUser.getRoles())) if (loginUser == null || CollectionUtils.isEmpty(loginUser.getRoles()))
{ {
return false; return false;
} }
for (String role : roles.split(ROLE_DELIMETER)) for (String role : roles.split(ROLE_DELIMETER))
{ {
if (hasRole(role)) if (hasRole(role))
{ {
return true; return true;
} }
} }
return false; return false;
} }
} }

@ -1,173 +1,173 @@
package cn.teammodel.security.utils; package cn.teammodel.security.utils;
import cn.teammodel.model.entity.User; import cn.teammodel.model.entity.User;
import cn.teammodel.model.entity.TmdUserDetail; import cn.teammodel.model.entity.TmdUserDetail;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts; import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* @author winter * @author winter
* @date 20221124 10:50 * @date 20221124 10:50
* @description jwt * @description jwt
*/ */
@Component @Component
@Slf4j @Slf4j
public class JwtTokenUtil { public class JwtTokenUtil {
private static final long NEVER_EXPIRE = 315360000; // 没有永不过期的api: 让时钟偏移十年 private static final long NEVER_EXPIRE = 315360000; // 没有永不过期的api: 让时钟偏移十年
@Value("${jwt.secret}") @Value("${jwt.secret}")
private String secret; private String secret;
/** /**
* token * token
* @param userDetails userDetails * @param userDetails userDetails
* @return token * @return token
*/ */
@Deprecated @Deprecated
public String generateToken(UserDetails userDetails){ public String generateToken(UserDetails userDetails){
Map<String, Object> claims = new HashMap<>(); Map<String, Object> claims = new HashMap<>();
// 添加 payload // 添加 payload
//claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername()); //claims.put(CLAIM_KEY_USERNAME,userDetails.getUsername());
//claims.put(CLAIM_KEY_CREATED,new Date()); //claims.put(CLAIM_KEY_CREATED,new Date());
return generateToken(claims); return generateToken(claims);
} }
@Deprecated @Deprecated
private String generateToken(Map<String,Object> claims){ private String generateToken(Map<String,Object> claims){
return Jwts.builder() return Jwts.builder()
.setClaims(claims) .setClaims(claims)
.signWith(SignatureAlgorithm.HS512,secret) .signWith(SignatureAlgorithm.HS512,secret)
.compact(); .compact();
} }
/** /**
* token * token
* @param token token * @param token token
* @return ,null * @return ,null
*/ */
@Deprecated @Deprecated
public String getUsernameFromToken(String token) { public String getUsernameFromToken(String token) {
String username = null; String username = null;
try { try {
Claims claims = getClaimsFromToken(token); Claims claims = getClaimsFromToken(token);
username = claims.getSubject(); username = claims.getSubject();
}catch (Exception e){ }catch (Exception e){
log.info("jwt验证出错:{}",token); log.info("jwt验证出错:{}",token);
} }
return username; return username;
} }
/** /**
* tokenClaimsnull * tokenClaimsnull
* @param token token * @param token token
* @return claimsnull * @return claimsnull
*/ */
private Claims getClaimsFromToken(String token) { private Claims getClaimsFromToken(String token) {
Claims claims = null; Claims claims = null;
try { try {
claims = Jwts.parser() claims = Jwts.parser()
.setSigningKey(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")) .setSigningKey(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"))
.setAllowedClockSkewSeconds(NEVER_EXPIRE) .setAllowedClockSkewSeconds(NEVER_EXPIRE)
.parseClaimsJws(token) .parseClaimsJws(token)
.getBody(); .getBody();
} catch (Exception e) { } catch (Exception e) {
log.warn("token 解析出错:{}",e.getMessage()); log.warn("token 解析出错:{}",e.getMessage());
} }
return claims; return claims;
} }
/** /**
* token * token
* @param token token * @param token token
* @return truefalse * @return truefalse
*/ */
public boolean validateToken(String token) { public boolean validateToken(String token) {
return !isTokenExpired(token); return !isTokenExpired(token);
} }
public boolean validateToken(HttpServletRequest request) { public boolean validateToken(HttpServletRequest request) {
String token = request.getHeader("x-auth-AuthToken"); String token = request.getHeader("x-auth-AuthToken");
if (StringUtils.isBlank(token)) { if (StringUtils.isBlank(token)) {
return false; return false;
} }
return validateToken(token); return validateToken(token);
} }
private boolean isTokenExpired(String token) { private boolean isTokenExpired(String token) {
Date expiredDate = getExpiredDateFromToken(token); Date expiredDate = getExpiredDateFromToken(token);
return expiredDate.before(new Date()); return expiredDate.before(new Date());
} }
private Date getExpiredDateFromToken(String token) { private Date getExpiredDateFromToken(String token) {
Claims claims = getClaimsFromToken(token); Claims claims = getClaimsFromToken(token);
return claims.getExpiration(); return claims.getExpiration();
} }
/** /**
* token , null * token , null
* @param request: * @param request:
* @return: cn.teammodel.model.entity.TmdUserDetail * @return: cn.teammodel.model.entity.TmdUserDetail
* @author: winter * @author: winter
* @date: 2023/11/10 10:24 * @date: 2023/11/10 10:24
* @description: * @description:
*/ */
public TmdUserDetail getValidUserDetail(HttpServletRequest request) { public TmdUserDetail getValidUserDetail(HttpServletRequest request) {
String token = request.getHeader("x-auth-AuthToken"); String token = request.getHeader("x-auth-AuthToken");
if (StringUtils.isBlank(token)) { if (StringUtils.isBlank(token)) {
return null; return null;
} }
Claims claims = getClaimsFromToken(token); Claims claims = getClaimsFromToken(token);
if (claims == null) { if (claims == null) {
return null; return null;
} }
// 组装 TmdUserDetail // 组装 TmdUserDetail
TmdUserDetail tmdUserDetail = new TmdUserDetail(); TmdUserDetail tmdUserDetail = new TmdUserDetail();
User user = new User(); User user = new User();
String id = claims.getSubject(); String id = claims.getSubject();
user.setId(id); user.setId(id);
user.setName(claims.get("name") == null ? null : claims.get("name", String.class)); user.setName(claims.get("name") == null ? null : claims.get("name", String.class));
user.setPicture(claims.get("picture") == null ? null : claims.get("picture", String.class)); user.setPicture(claims.get("picture") == null ? null : claims.get("picture", String.class));
user.setStandard(claims.get("standard") == null ? null : claims.get("standard", String.class)); user.setStandard(claims.get("standard") == null ? null : claims.get("standard", String.class));
user.setScope(claims.get("scope") == null ? null : claims.get("scope", String.class)); user.setScope(claims.get("scope") == null ? null : claims.get("scope", String.class));
user.setWebsite(claims.get("website") == null ? null : claims.get("website", String.class)); user.setWebsite(claims.get("website") == null ? null : claims.get("website", String.class));
user.setArea(claims.get("area") == null ? null : claims.get("area", String.class)); user.setArea(claims.get("area") == null ? null : claims.get("area", String.class));
// 取出 roles 和 permissions // 取出 roles 和 permissions
Set<String> roleSet = convertToArray(claims.get("roles")); Set<String> roleSet = convertToArray(claims.get("roles"));
Set<String> permissionSet = convertToArray(claims.get("permissions")); Set<String> permissionSet = convertToArray(claims.get("permissions"));
user.setRoles(roleSet); user.setRoles(roleSet);
user.setPermissions(permissionSet); user.setPermissions(permissionSet);
tmdUserDetail.setUser(user); tmdUserDetail.setUser(user);
return tmdUserDetail; return tmdUserDetail;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Set<String> convertToArray(Object o) { private Set<String> convertToArray(Object o) {
if (o == null) { if (o == null) {
return Collections.emptySet(); return Collections.emptySet();
} }
if (o instanceof String) { if (o instanceof String) {
if (StringUtils.isNotBlank((String)o)) { if (StringUtils.isNotBlank((String)o)) {
return Arrays.stream(((String) o).split(" ")).collect(Collectors.toSet()); return Arrays.stream(((String) o).split(" ")).collect(Collectors.toSet());
} }
return Collections.emptySet(); return Collections.emptySet();
} }
if (o instanceof Collection) { if (o instanceof Collection) {
return new HashSet<>(((Collection<String>) o)); return new HashSet<>(((Collection<String>) o));
} }
return Collections.emptySet(); return Collections.emptySet();
} }
} }

@ -1,107 +1,107 @@
package cn.teammodel.security.utils; package cn.teammodel.security.utils;
import cn.teammodel.config.exception.ServiceException; import cn.teammodel.config.exception.ServiceException;
import cn.teammodel.model.entity.User; import cn.teammodel.model.entity.User;
import cn.teammodel.model.entity.TmdUserDetail; import cn.teammodel.model.entity.TmdUserDetail;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/** /**
* *
* *
* @author winter * @author winter
*/ */
public class SecurityUtils public class SecurityUtils
{ {
/** /**
* ID * ID
**/ **/
public static String getUserId() public static String getUserId()
{ {
try try
{ {
return getLoginUser().getId(); return getLoginUser().getId();
} }
catch (Exception e) catch (Exception e)
{ {
throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED.value()); throw new ServiceException("获取用户ID异常", HttpStatus.UNAUTHORIZED.value());
} }
} }
/** /**
* *
**/ **/
public static String getUsername() public static String getUsername()
{ {
try try
{ {
return getLoginUser().getName(); return getLoginUser().getName();
} }
catch (Exception e) catch (Exception e)
{ {
throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED.value()); throw new ServiceException("获取用户账户异常", HttpStatus.UNAUTHORIZED.value());
} }
} }
/** /**
* *
**/ **/
public static User getLoginUser() public static User getLoginUser()
{ {
try try
{ {
return ((TmdUserDetail) getAuthentication().getPrincipal()).getUser(); return ((TmdUserDetail) getAuthentication().getPrincipal()).getUser();
} }
catch (Exception e) catch (Exception e)
{ {
throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED.value()); throw new ServiceException("获取用户信息异常", HttpStatus.UNAUTHORIZED.value());
} }
} }
/** /**
* Authentication * Authentication
*/ */
public static Authentication getAuthentication() public static Authentication getAuthentication()
{ {
return SecurityContextHolder.getContext().getAuthentication(); return SecurityContextHolder.getContext().getAuthentication();
} }
/** /**
* BCryptPasswordEncoder * BCryptPasswordEncoder
* *
* @param password * @param password
* @return * @return
*/ */
public static String encryptPassword(String password) public static String encryptPassword(String password)
{ {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.encode(password); return passwordEncoder.encode(password);
} }
/** /**
* *
* *
* @param rawPassword * @param rawPassword
* @param encodedPassword * @param encodedPassword
* @return * @return
*/ */
public static boolean matchesPassword(String rawPassword, String encodedPassword) public static boolean matchesPassword(String rawPassword, String encodedPassword)
{ {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
return passwordEncoder.matches(rawPassword, encodedPassword); return passwordEncoder.matches(rawPassword, encodedPassword);
} }
/** /**
* *
* *
* @param userId ID * @param userId ID
* @return * @return
*/ */
public static boolean isAdmin(Long userId) public static boolean isAdmin(Long userId)
{ {
return userId != null && 1L == userId; return userId != null && 1L == userId;
} }
} }

@ -1,20 +1,21 @@
spring: spring:
cloud: cloud:
azure: azure:
cosmos: cosmos:
endpoint: https://cdhabookdep-free.documents.azure.cn:443 endpoint: https://cdhabookdep-free.documents.azure.cn:443
database: TEAMModelOS database: TEAMModelOS
key: v07dMthUjNk8AbwI8698AXHPbXBaaADgtgdYf4sFGXohei6aD2doq6XV45sLMGpDtDDpENAnlGkEUBu9RaAhpg== key: v07dMthUjNk8AbwI8698AXHPbXBaaADgtgdYf4sFGXohei6aD2doq6XV45sLMGpDtDDpENAnlGkEUBu9RaAhpg==
security: security:
oauth2: oauth2:
resourceserver: resourceserver:
jwt: jwt:
issuer-uri: https://login.partner.microsoftonline.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b/v2.0 issuer-uri: https://login.partner.microsoftonline.cn/4807e9cf-87b8-4174-aa5b-e76497d7392b/v2.0
audiences: 72643704-b2e7-4b26-b881-bd5865e7a7a5 audiences: 72643704-b2e7-4b26-b881-bd5865e7a7a5
jwt: jwt:
secret: fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA= secret: fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=
dingding: # 钉钉 webhook
ding:
server-url: https://oapi.dingtalk.com/robot/send?access_token=32d9b24f69c2c4fd7c2dab43268b6258a7214d2620e0805d7b6d1429003b64b6 server-url: https://oapi.dingtalk.com/robot/send?access_token=32d9b24f69c2c4fd7c2dab43268b6258a7214d2620e0805d7b6d1429003b64b6

@ -1,13 +1,19 @@
package cn.teammodel; package cn.teammodel;
import cn.teammodel.manager.DingAlertNotifier;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest @SpringBootTest
class TeamModelExtensionApplicationTests { class TeamModelExtensionApplicationTests {
@Autowired
private DingAlertNotifier notifier;
@Test @Test
void contextLoads() { void contextLoads() {
notifier.send("告警: 测试消息推送封装模块");
} }
} }

@ -1,66 +1,95 @@
package cn.teammodel; package cn.teammodel;
import com.dingtalk.api.DefaultDingTalkClient; import cn.teammodel.common.R;
import com.dingtalk.api.DingTalkClient; import cn.teammodel.model.entity.User;
import com.dingtalk.api.request.OapiRobotSendRequest; import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.response.OapiRobotSendResponse; import com.dingtalk.api.DingTalkClient;
import com.taobao.api.ApiException; import com.dingtalk.api.request.OapiRobotSendRequest;
import org.junit.jupiter.api.Test; import com.dingtalk.api.response.OapiRobotSendResponse;
import com.taobao.api.ApiException;
import java.util.Arrays; import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
/**
* @author winter import java.util.Arrays;
* @create 2023-11-10 10:42
*/ /**
public class TestWithoutSpring { * @author winter
@Test * @create 2023-11-10 10:42
public void testJwtDecode() { */
// Claims claims = null; @Slf4j
// try { public class TestWithoutSpring {
// Jwts.parser() @Test
// public void testJwtDecode() {
// // Claims claims = null;
//// claims = Jwts.parser() // try {
//// .setSigningKey("fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=") // Jwts.parser()
//// .parseClaimsJws("eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0LnRlYW1tb2RlbC5jbiIsInN1YiI6IjE1OTUzMjEzNTQiLCJhenAiOiJoYmNuIiwiZXhwIjoxNjk5NTg2MzM1LCJuYW1lIjoi572X6ICB5biIIiwicGljdHVyZSI6Imh0dHBzOi8vY29yZXN0b3JhZ2VzZXJ2aWNlLmJsb2IuY29yZS5jaGluYWNsb3VkYXBpLmNuL2FjY291bnQvYXZhdGFyLzE1OTUzMjEzNTQiLCJyb2xlcyI6WyJ0ZWFjaGVyIiwiYWRtaW4iLCJhcmVhIl0sInBlcm1pc3Npb25zIjpbXSwic3RhbmRhcmQiOiJzdGFuZGFyZDEiLCJzY29wZSI6InRlYWNoZXIiLCJhcmVhIjoiNjllM2Q0MTMtNTBhMS00ZjVlLTg0NGEtZTBmN2M5NjIyZWEzIiwid2Vic2l0ZSI6IklFUyJ9.4NdqwDdDQcyberEAirX0srOIb1ADXLJfP-a9qNXb0yw") //
//// .getBody(); //
// } catch (Exception e) { //// claims = Jwts.parser()
// e.printStackTrace(); //// .setSigningKey("fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=")
// } //// .parseClaimsJws("eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0LnRlYW1tb2RlbC5jbiIsInN1YiI6IjE1OTUzMjEzNTQiLCJhenAiOiJoYmNuIiwiZXhwIjoxNjk5NTg2MzM1LCJuYW1lIjoi572X6ICB5biIIiwicGljdHVyZSI6Imh0dHBzOi8vY29yZXN0b3JhZ2VzZXJ2aWNlLmJsb2IuY29yZS5jaGluYWNsb3VkYXBpLmNuL2FjY291bnQvYXZhdGFyLzE1OTUzMjEzNTQiLCJyb2xlcyI6WyJ0ZWFjaGVyIiwiYWRtaW4iLCJhcmVhIl0sInBlcm1pc3Npb25zIjpbXSwic3RhbmRhcmQiOiJzdGFuZGFyZDEiLCJzY29wZSI6InRlYWNoZXIiLCJhcmVhIjoiNjllM2Q0MTMtNTBhMS00ZjVlLTg0NGEtZTBmN2M5NjIyZWEzIiwid2Vic2l0ZSI6IklFUyJ9.4NdqwDdDQcyberEAirX0srOIb1ADXLJfP-a9qNXb0yw")
} //// .getBody();
// } catch (Exception e) {
@Test // e.printStackTrace();
public void testDingDing() throws ApiException { // }
DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token=32d9b24f69c2c4fd7c2dab43268b6258a7214d2620e0805d7b6d1429003b64b6"); }
OapiRobotSendRequest request = new OapiRobotSendRequest();
request.setMsgtype("text"); @Test
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text(); public void testDingDing() throws ApiException {
text.setContent("测试告警文本消息"); DingTalkClient client = new DefaultDingTalkClient("https://oapi.dingtalk.com/robot/send?access_token=32d9b24f69c2c4fd7c2dab43268b6258a7214d2620e0805d7b6d1429003b64b6");
request.setText(text); OapiRobotSendRequest request = new OapiRobotSendRequest();
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At(); request.setMsgtype("text");
at.setAtMobiles(Arrays.asList("15196506772")); OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
// isAtAll类型如果不为Boolean请升级至最新SDK text.setContent("测试告警文本消息");
at.setIsAtAll(true); request.setText(text);
at.setAtUserIds(Arrays.asList("109929","32099")); OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
request.setAt(at); at.setAtMobiles(Arrays.asList("15196506772"));
// isAtAll类型如果不为Boolean请升级至最新SDK
request.setMsgtype("link"); at.setIsAtAll(true);
OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link(); at.setAtUserIds(Arrays.asList("109929","32099"));
link.setMessageUrl("https://www.dingtalk.com/"); request.setAt(at);
link.setPicUrl("");
link.setTitle("告警时代的火车向前开"); request.setMsgtype("link");
link.setText("告警这个即将发布的新版本创始人xx称它为红树林。而在此之前每当面临重大升级产品经理们都会取一个应景的代号这一次为什么是红树林"); OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
request.setLink(link); link.setMessageUrl("https://www.dingtalk.com/");
link.setPicUrl("");
request.setMsgtype("markdown"); link.setTitle("告警时代的火车向前开");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown(); link.setText("告警这个即将发布的新版本创始人xx称它为红树林。而在此之前每当面临重大升级产品经理们都会取一个应景的代号这一次为什么是红树林");
markdown.setTitle("杭州天气"); request.setLink(link);
markdown.setText("#### 告警杭州天气 @156xxxx8827\n" +
"> 9度西北风1级空气良89相对温度73%\n\n" + request.setMsgtype("markdown");
"> ![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png)\n" + OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
"> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) \n"); markdown.setTitle("杭州天气");
request.setMarkdown(markdown); markdown.setText("#### 告警杭州天气 @156xxxx8827\n" +
OapiRobotSendResponse response = client.execute(request); "> 9度西北风1级空气良89相对温度73%\n\n" +
} "> ![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png)\n" +
} "> ###### 10点20分发布 [天气](http://www.thinkpage.cn/) \n");
request.setMarkdown(markdown);
OapiRobotSendResponse response = client.execute(request);
}
@Test
public void testException() {
try {
throw new Exception("出现异常");
} catch (Exception e) {
//e.printStackTrace();
log.error("error: {}", e);
//System.out.println(e.getMessage());
//System.out.println(e.getCause());
//System.out.println(e.getStackTrace());
}
}
@Test
public void testResult() {
User user = new User();
user.setId("ds");
user.setName("ds");
user.setPicture("s");
user.setStandard("s");
user.setScope("ad");
user.setWebsite("dsa");
user.setArea("");
System.out.println(R.success(user));
}
}

Loading…
Cancel
Save