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

11111
winter 1 year ago
parent 46fa7bf45e
commit f0a9ba9243

@ -1,8 +1,10 @@
# TeamModel extension
> SpringBoot base version of TeamModel extension
>
> **注意**: 所有复盘输出均已脱敏,不包含任何业务,密码等关键信息
## 迁移目录:
- Azure OIDC(SSO) 迁移
- id-token(jwt) 验证迁移 (出现语言框架之间的签名算法规范问题,解决见: [输出复盘](https://juejin.cn/post/7300036605099163702))
# TeamModel extension
> SpringBoot base version of TeamModel extension
>
> **注意**: 所有复盘输出均已脱敏,不包含任何业务,密码等关键信息
## 迁移目录:
- Azure OIDC(SSO) 迁移
- 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;
public class R {
private Integer code;
private String message;
private String data;
public R(Integer code, String message, String data) {
this.code = code;
this.message = message;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
}
package cn.teammodel.common;
import lombok.Data;
import java.io.Serializable;
@Data
public class R<T> implements Serializable {
private int code;
private T data;
private String message;
public R(int code, T data, String message) {
this.code = code;
this.data = data;
this.message = message;
}
public R(int code, T data) {
this(code, data, "");
}
public R(ErrorCode errorCode) {
this(errorCode.getCode(), null, errorCode.getMessage());
}
public R(ErrorCode errorCode, T data) {
this(errorCode.getCode(), data, errorCode.getMessage());
}
public static <T> R<T> success(T data) {
return new R<>(ErrorCode.SUCCESS, 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;
/**
*
*
* @author winter
*/
public final class ServiceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
*
*/
private Integer code;
/**
*
*/
private String message;
/**
*
*/
private String detailMessage;
/**
*
*/
public ServiceException()
{
}
public ServiceException(String message)
{
this.message = message;
}
public ServiceException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public String getDetailMessage()
{
return detailMessage;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
public ServiceException setMessage(String message)
{
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
package cn.teammodel.config.exception;
/**
*
*
* @author winter
*/
public final class ServiceException extends RuntimeException
{
private static final long serialVersionUID = 1L;
/**
*
*/
private Integer code;
/**
*
*/
private String message;
/**
*
*/
private String detailMessage;
/**
*
*/
public ServiceException()
{
}
public ServiceException(String message)
{
this.message = message;
}
public ServiceException(String message, Integer code)
{
this.message = message;
this.code = code;
}
public String getDetailMessage()
{
return detailMessage;
}
@Override
public String getMessage()
{
return message;
}
public Integer getCode()
{
return code;
}
public ServiceException setMessage(String message)
{
this.message = message;
return this;
}
public ServiceException setDetailMessage(String detailMessage)
{
this.detailMessage = detailMessage;
return this;
}
}

@ -1,20 +1,20 @@
package cn.teammodel.controller;
import cn.teammodel.common.R;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class HelloController {
@GetMapping("hello")
@PreAuthorize("@ss.hasRole('admin')")
public R hello() {
System.out.println(SecurityContextHolder.getContext().getAuthentication());
return new R(200, "success","hello world");
}
package cn.teammodel.controller;
import cn.teammodel.common.R;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class HelloController {
@GetMapping("hello")
@PreAuthorize("@ss.hasRole('admin')")
public R hello() {
System.out.println(SecurityContextHolder.getContext().getAuthentication());
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;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* @author winter
* @create 2023-11-09 15:28
*/
@Data
public class TmdUserDetail implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return true;
}
}
package cn.teammodel.model.entity;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* @author winter
* @create 2023-11-09 15:28
*/
@Data
public class TmdUserDetail implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return null;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return true;
}
}

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

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

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

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

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

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

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

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

@ -1,66 +1,95 @@
package cn.teammodel;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.taobao.api.ApiException;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
/**
* @author winter
* @create 2023-11-10 10:42
*/
public class TestWithoutSpring {
@Test
public void testJwtDecode() {
// Claims claims = null;
// try {
// Jwts.parser()
//
//
//// claims = Jwts.parser()
//// .setSigningKey("fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=")
//// .parseClaimsJws("eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0LnRlYW1tb2RlbC5jbiIsInN1YiI6IjE1OTUzMjEzNTQiLCJhenAiOiJoYmNuIiwiZXhwIjoxNjk5NTg2MzM1LCJuYW1lIjoi572X6ICB5biIIiwicGljdHVyZSI6Imh0dHBzOi8vY29yZXN0b3JhZ2VzZXJ2aWNlLmJsb2IuY29yZS5jaGluYWNsb3VkYXBpLmNuL2FjY291bnQvYXZhdGFyLzE1OTUzMjEzNTQiLCJyb2xlcyI6WyJ0ZWFjaGVyIiwiYWRtaW4iLCJhcmVhIl0sInBlcm1pc3Npb25zIjpbXSwic3RhbmRhcmQiOiJzdGFuZGFyZDEiLCJzY29wZSI6InRlYWNoZXIiLCJhcmVhIjoiNjllM2Q0MTMtNTBhMS00ZjVlLTg0NGEtZTBmN2M5NjIyZWEzIiwid2Vic2l0ZSI6IklFUyJ9.4NdqwDdDQcyberEAirX0srOIb1ADXLJfP-a9qNXb0yw")
//// .getBody();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
@Test
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");
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent("测试告警文本消息");
request.setText(text);
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
at.setAtMobiles(Arrays.asList("15196506772"));
// isAtAll类型如果不为Boolean请升级至最新SDK
at.setIsAtAll(true);
at.setAtUserIds(Arrays.asList("109929","32099"));
request.setAt(at);
request.setMsgtype("link");
OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
link.setMessageUrl("https://www.dingtalk.com/");
link.setPicUrl("");
link.setTitle("告警时代的火车向前开");
link.setText("告警这个即将发布的新版本创始人xx称它为红树林。而在此之前每当面临重大升级产品经理们都会取一个应景的代号这一次为什么是红树林");
request.setLink(link);
request.setMsgtype("markdown");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("杭州天气");
markdown.setText("#### 告警杭州天气 @156xxxx8827\n" +
"> 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);
}
}
package cn.teammodel;
import cn.teammodel.common.R;
import cn.teammodel.model.entity.User;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiRobotSendRequest;
import com.dingtalk.api.response.OapiRobotSendResponse;
import com.taobao.api.ApiException;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
/**
* @author winter
* @create 2023-11-10 10:42
*/
@Slf4j
public class TestWithoutSpring {
@Test
public void testJwtDecode() {
// Claims claims = null;
// try {
// Jwts.parser()
//
//
//// claims = Jwts.parser()
//// .setSigningKey("fXO6ko/qyXeYrkecPeKdgXnuLXf9vMEtnBC9OB3s+aA=")
//// .parseClaimsJws("eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJ0ZXN0LnRlYW1tb2RlbC5jbiIsInN1YiI6IjE1OTUzMjEzNTQiLCJhenAiOiJoYmNuIiwiZXhwIjoxNjk5NTg2MzM1LCJuYW1lIjoi572X6ICB5biIIiwicGljdHVyZSI6Imh0dHBzOi8vY29yZXN0b3JhZ2VzZXJ2aWNlLmJsb2IuY29yZS5jaGluYWNsb3VkYXBpLmNuL2FjY291bnQvYXZhdGFyLzE1OTUzMjEzNTQiLCJyb2xlcyI6WyJ0ZWFjaGVyIiwiYWRtaW4iLCJhcmVhIl0sInBlcm1pc3Npb25zIjpbXSwic3RhbmRhcmQiOiJzdGFuZGFyZDEiLCJzY29wZSI6InRlYWNoZXIiLCJhcmVhIjoiNjllM2Q0MTMtNTBhMS00ZjVlLTg0NGEtZTBmN2M5NjIyZWEzIiwid2Vic2l0ZSI6IklFUyJ9.4NdqwDdDQcyberEAirX0srOIb1ADXLJfP-a9qNXb0yw")
//// .getBody();
// } catch (Exception e) {
// e.printStackTrace();
// }
}
@Test
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");
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent("测试告警文本消息");
request.setText(text);
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
at.setAtMobiles(Arrays.asList("15196506772"));
// isAtAll类型如果不为Boolean请升级至最新SDK
at.setIsAtAll(true);
at.setAtUserIds(Arrays.asList("109929","32099"));
request.setAt(at);
request.setMsgtype("link");
OapiRobotSendRequest.Link link = new OapiRobotSendRequest.Link();
link.setMessageUrl("https://www.dingtalk.com/");
link.setPicUrl("");
link.setTitle("告警时代的火车向前开");
link.setText("告警这个即将发布的新版本创始人xx称它为红树林。而在此之前每当面临重大升级产品经理们都会取一个应景的代号这一次为什么是红树林");
request.setLink(link);
request.setMsgtype("markdown");
OapiRobotSendRequest.Markdown markdown = new OapiRobotSendRequest.Markdown();
markdown.setTitle("杭州天气");
markdown.setText("#### 告警杭州天气 @156xxxx8827\n" +
"> 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