feat: initial commit

This commit is contained in:
gin
2026-05-07 18:39:00 +08:00
commit cdee21ee8e
653 changed files with 63946 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>agileboot</artifactId>
<groupId>com.agileboot</groupId>
<version>1.0.0</version>
</parent>
<packaging>jar</packaging>
<modelVersion>4.0.0</modelVersion>
<artifactId>agileboot-api</artifactId>
<description>
外部API
</description>
<dependencies>
<!-- 核心模块-->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-infrastructure</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--使用undertow依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!-- 业务领域 -->
<dependency>
<groupId>com.agileboot</groupId>
<artifactId>agileboot-domain</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,32 @@
package com.agileboot.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
/**
* 启动程序 定制banner.txt的网站
* <a href="http://patorjk.com/software/taag">http://patorjk.com/software/taag</a>
* <a href="http://www.network-science.de/ascii/">http://www.network-science.de/ascii/</a>
* <a href="http://www.degraeve.com/img2txt.php">http://www.degraeve.com/img2txt.php</a>
* <a href="http://life.chacuo.net/convertfont2char">http://life.chacuo.net/convertfont2char</a>
*
* @author valarchie
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@ComponentScan(basePackages = "com.agileboot.*")
public class AgileBooApiApplication {
public static void main(String[] args) {
SpringApplication.run(AgileBooApiApplication.class, args);
String successMsg = " ____ _ _ __ _ _ \n"
+ " / ___| | |_ __ _ _ __ | |_ _ _ _ __ ___ _ _ ___ ___ ___ ___ ___ / _| _ _ | || |\n"
+ " \\___ \\ | __|/ _` || '__|| __| | | | || '_ \\ / __|| | | | / __|/ __|/ _ \\/ __|/ __|| |_ | | | || || |\n"
+ " ___) || |_| (_| || | | |_ | |_| || |_) | \\__ \\| |_| || (__| (__| __/\\__ \\\\__ \\| _|| |_| || ||_|\n"
+ " |____/ \\__|\\__,_||_| \\__| \\__,_|| .__/ |___/ \\__,_| \\___|\\___|\\___||___/|___/|_| \\__,_||_|(_)\n"
+ " |_| ";
System.out.println(successMsg);
}
}
@@ -0,0 +1,25 @@
package com.agileboot.api.controller;
import com.agileboot.common.core.base.BaseController;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 调度日志操作处理
*
* @author valarchie
*/
@RestController
@RequestMapping("/api/order")
public class OrderController extends BaseController {
/**
* 访问首页,提示语
*/
@RequestMapping("/")
public String index() {
return "暂无订单";
}
}
@@ -0,0 +1,39 @@
package com.agileboot.api.controller.app;
import com.agileboot.api.customize.service.JwtTokenService;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import lombok.AllArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 调度日志操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/app")
@AllArgsConstructor
public class AppController extends BaseController {
private final JwtTokenService jwtTokenService;
/**
* 访问首页,提示语
*/
@PreAuthorize("hasAuthority('annie')")
@GetMapping("/list")
public ResponseDTO<?> appLogin() {
return ResponseDTO.ok();
}
}
@@ -0,0 +1,38 @@
package com.agileboot.api.controller.common;
import cn.hutool.core.map.MapUtil;
import com.agileboot.api.customize.service.JwtTokenService;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import java.util.Map;
import lombok.AllArgsConstructor;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 调度日志操作处理
*
* @author ruoyi
*/
@RestController
@RequestMapping("/common")
@AllArgsConstructor
public class LoginController extends BaseController {
private final JwtTokenService jwtTokenService;
/**
* 访问首页,提示语
*/
@PostMapping("/app/{appId}/login")
public ResponseDTO<String> appLogin() {
String token = jwtTokenService.generateToken(MapUtil.of("token", "user1"));
return ResponseDTO.ok(token);
}
}
@@ -0,0 +1,52 @@
package com.agileboot.api.customize.config;
import com.agileboot.api.customize.service.JwtTokenService;
import com.agileboot.infrastructure.user.app.AppLoginUser;
import io.jsonwebtoken.Claims;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* token过滤器 验证token有效性
* 继承OncePerRequestFilter类的话 可以确保只执行filter一次, 避免执行多次
* @author valarchie
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtTokenService jwtTokenService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String tokenFromRequest = jwtTokenService.getTokenFromRequest(request);
if (tokenFromRequest != null) {
Claims claims = jwtTokenService.parseToken(tokenFromRequest);
String token = (String) claims.get("token");
// 根据token去查缓存里面 有没有对应的App用户
// 没有的话 再去数据库中查询
if (token != null && token.equals("user1")) {
AppLoginUser loginUser = new AppLoginUser(23232323L, false, "dasdsadsds");
loginUser.grantAppPermission("annie");
UsernamePasswordAuthenticationToken suer1 = new UsernamePasswordAuthenticationToken(loginUser, null,
loginUser.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(suer1);
}
}
filterChain.doFilter(request, response);
}
}
@@ -0,0 +1,85 @@
package com.agileboot.api.customize.config;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode.Client;
import com.agileboot.common.utils.ServletHolderUtil;
import com.agileboot.common.utils.jackson.JacksonUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.filter.CorsFilter;
/**
* 主要配置登录流程逻辑涉及以下几个类
* @author valarchie
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
/**
* token认证过滤器
*/
private final JwtAuthenticationFilter jwtTokenFilter;
/**
* 跨域过滤器
*/
private final CorsFilter corsFilter;
/**
* 登录异常处理类
* 用户未登陆的话 在这个Bean中处理
*/
@Bean
public AuthenticationEntryPoint customAuthenticationEntryPoint() {
return (request, response, exception) -> {
ResponseDTO<Void> responseDTO = ResponseDTO.fail(
new ApiException(Client.COMMON_NO_AUTHORIZATION, request.getRequestURI())
);
ServletHolderUtil.renderString(response, JacksonUtil.to(responseDTO));
};
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable()
// 不配这个错误处理的话 会直接返回403
.exceptionHandling().authenticationEntryPoint(customAuthenticationEntryPoint())
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用 session
.and()
.authorizeRequests()
.antMatchers("/common/**").permitAll()
.anyRequest().authenticated()
.and()
// 禁用 X-Frame-Options 响应头。下面是具体解释:
// X-Frame-Options 是一个 HTTP 响应头,用于防止网页被嵌入到其他网页的 <frame>、<iframe> 或 <object> 标签中,从而可以减少点击劫持攻击的风险
.headers().frameOptions().disable()
.and()
.formLogin().disable();
httpSecurity.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 添加CORS filter
httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationFilter.class);
return httpSecurity.build();
}
}
@@ -0,0 +1,127 @@
package com.agileboot.api.customize.service;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.constant.Constants.Token;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.common.cache.RedisCacheService;
import com.agileboot.infrastructure.user.web.SystemLoginUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* token验证处理
*
* @author valarchie
*/
@Component
@Slf4j
@Data
@RequiredArgsConstructor
public class JwtTokenService {
/**
* 自定义令牌标识
*/
@Value("${token.header}")
private String header;
/**
* 令牌秘钥
*/
@Value("${token.secret}")
private String secret;
private final RedisCacheService redisCache;
/**
* 获取用户身份信息
*
* @return 用户信息
*/
public SystemLoginUser getLoginUser(HttpServletRequest request) {
// 获取请求携带的令牌
String token = getTokenFromRequest(request);
if (StrUtil.isNotEmpty(token)) {
try {
Claims claims = parseToken(token);
// 解析对应的权限以及用户信息
String uuid = (String) claims.get(Token.LOGIN_USER_KEY);
return redisCache.loginUserCache.getObjectOnlyInCacheById(uuid);
} catch (SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException jwtException) {
log.error("parse token failed.", jwtException);
throw new ApiException(jwtException, ErrorCode.Client.INVALID_TOKEN);
} catch (Exception e) {
log.error("fail to get cached user from redis", e);
throw new ApiException(e, ErrorCode.Client.TOKEN_PROCESS_FAILED, e.getMessage());
}
}
return null;
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
public String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
public Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
/**
* 从令牌中获取用户名
*
* @param token 令牌
* @return 用户名
*/
private String getUsernameFromToken(String token) {
Claims claims = parseToken(token);
return claims.getSubject();
}
/**
* 获取请求token
*
* @return token
*/
public String getTokenFromRequest(HttpServletRequest request) {
String token = request.getHeader(header);
if (StrUtil.isNotEmpty(token) && token.startsWith(Token.PREFIX)) {
token = StrUtil.stripIgnoreCase(token, Token.PREFIX, null);
}
return token;
}
}
@@ -0,0 +1,12 @@
package com.agileboot.api.customize.util;
public class ApiEncryptor {
public static void main(String[] args) {
}
}
@@ -0,0 +1,28 @@
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 8090
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数,默认为100
accept-count: 1000
threads:
# tomcat最大线程数,默认为200
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
# Spring配置
spring:
profiles:
active: basic,dev