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,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,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,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("告警: 测试消息推送封装模块");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in new issue