首次提交

This commit is contained in:
DelLevin-Home
2026-01-13 10:06:37 +08:00
commit 71de5323aa
517 changed files with 54169 additions and 0 deletions

7
IDEA/api/Dockerfile Normal file
View File

@@ -0,0 +1,7 @@
FROM java:8
EXPOSE 8081
VOLUME /tmp
ADD renren-api.jar /app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-jar","/app.jar"]

27
IDEA/api/db/dm8.sql Normal file
View File

@@ -0,0 +1,27 @@
CREATE TABLE tb_user (
id bigint NOT NULL,
username varchar(50) NOT NULL,
mobile varchar(20) NOT NULL,
password varchar(64),
create_date datetime,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX idx_user_username on tb_user(username);
CREATE TABLE tb_token (
id bigint NOT NULL,
user_id bigint NOT NULL,
token varchar(100) NOT NULL,
expire_date datetime,
update_date datetime,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX idx_token_user_id on tb_token(user_id);
CREATE UNIQUE INDEX idx_token on tb_token(token);
-- 账号13612345678 密码admin
INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875800000168, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now());
commit;

25
IDEA/api/db/mysql.sql Normal file
View File

@@ -0,0 +1,25 @@
-- 用户表
CREATE TABLE tb_user (
id bigint NOT NULL COMMENT 'id',
username varchar(50) NOT NULL COMMENT '用户名',
mobile varchar(20) NOT NULL COMMENT '手机号',
password varchar(64) COMMENT '密码',
create_date datetime COMMENT '创建时间',
PRIMARY KEY (id),
UNIQUE INDEX (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户';
-- 用户Token表
CREATE TABLE tb_token (
id bigint NOT NULL COMMENT 'id',
user_id bigint NOT NULL COMMENT '用户ID',
token varchar(100) NOT NULL COMMENT 'token',
expire_date datetime COMMENT '过期时间',
update_date datetime COMMENT '更新时间',
PRIMARY KEY (id),
UNIQUE INDEX (user_id),
UNIQUE INDEX (token)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户Token';
-- 账号13612345678 密码admin
INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now());

24
IDEA/api/db/oracle.sql Normal file
View File

@@ -0,0 +1,24 @@
CREATE TABLE tb_user (
id NUMBER(20, 0) NOT NULL,
username varchar(50) NOT NULL,
mobile varchar(20) NOT NULL,
password varchar(64),
create_date date,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX idx_user_username on tb_user(username);
CREATE TABLE tb_token (
id NUMBER(20, 0) NOT NULL,
user_id NUMBER(20, 0) NOT NULL,
token varchar(100) NOT NULL,
expire_date date,
update_date date,
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX idx_token_user_id on tb_token(user_id);
CREATE UNIQUE INDEX idx_token on tb_token(token);
-- 账号13612345678 密码admin
INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', CURRENT_DATE);

View File

@@ -0,0 +1,24 @@
CREATE TABLE tb_user (
id int8 NOT NULL,
username varchar(50) NOT NULL,
mobile varchar(20) NOT NULL,
password varchar(64),
create_date timestamp,
PRIMARY KEY (id),
UNIQUE (username)
);
CREATE TABLE tb_token (
id int8 NOT NULL,
user_id int8 NOT NULL,
token varchar(100) NOT NULL,
expire_date timestamp,
update_date timestamp,
PRIMARY KEY (id),
UNIQUE (user_id),
UNIQUE (token)
);
-- 账号13612345678 密码admin
INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', now());

24
IDEA/api/db/sqlserver.sql Normal file
View File

@@ -0,0 +1,24 @@
CREATE TABLE tb_user (
id bigint NOT NULL,
username varchar(50) NOT NULL,
mobile varchar(20) NOT NULL,
password varchar(64),
create_date datetime,
PRIMARY KEY (id),
UNIQUE (username)
);
CREATE TABLE tb_token (
id bigint NOT NULL,
user_id bigint NOT NULL,
token varchar(100) NOT NULL,
expire_date datetime,
update_date datetime,
PRIMARY KEY (id),
UNIQUE (user_id),
UNIQUE (token)
);
-- 账号13612345678 密码admin
INSERT INTO tb_user (id, username, mobile, password, create_date) VALUES (1067246875900000001, 'mark', '13612345678', '8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918', getdate());

41
IDEA/api/pom.xml Normal file
View File

@@ -0,0 +1,41 @@
<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>
<groupId>io.renren</groupId>
<artifactId>person-sys</artifactId>
<version>5.5.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api</artifactId>
<packaging>jar</packaging>
<description>renren-api</description>
<dependencies>
<dependency>
<groupId>io.renren</groupId>
<artifactId>common</artifactId>
<version>5.5.0</version>
</dependency>
<dependency>
<groupId>io.renren</groupId>
<artifactId>dynamic-datasource</artifactId>
<version>5.5.0</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>true</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
/**
* renren-api
*
* @author Mark sunlightcs@gmail.com
*/
@SpringBootApplication
public class ApiApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(ApiApplication.class);
}
}

View File

@@ -0,0 +1,21 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.annotation;
import java.lang.annotation.*;
/**
* 登录效验
* @author Mark sunlightcs@gmail.com
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Login {
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 登录用户信息
*
* @author Mark sunlightcs@gmail.com
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginUser {
}

View File

@@ -0,0 +1,35 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.config;
import top.iletter.common.xss.XssFilter;
import jakarta.servlet.DispatcherType;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Filter配置
*
* @author Mark sunlightcs@gmail.com
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean xssFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setDispatcherTypes(DispatcherType.REQUEST);
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
return registration;
}
}

View File

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.config;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus配置
*
* @author Mark sunlightcs@gmail.com
*/
@Configuration
public class MybatisPlusConfig {
/**
* 配置分页等
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
// 乐观锁
mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
// 防止全表更新与删除
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.config;
import top.iletter.common.constant.Constant;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
@Configuration
public class SwaggerConfig {
@Bean
public OpenAPI createRestApi() {
return new OpenAPI()
.info(apiInfo())
.security(security());
}
private Info apiInfo() {
return new Info()
.title("人人开源")
.description("renren-api文档")
.version("5.x");
}
private List<SecurityRequirement> security() {
SecurityRequirement key = new SecurityRequirement();
key.addList(Constant.TOKEN_HEADER, Constant.TOKEN_HEADER);
List<SecurityRequirement> list = new ArrayList<>();
list.add(key);
return list;
}
}

View File

@@ -0,0 +1,86 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.config;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import top.iletter.common.utils.DateUtils;
import top.iletter.interceptor.AuthorizationInterceptor;
import top.iletter.resolver.LoginUserHandlerMethodArgumentResolver;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.TimeZone;
/**
* MVC配置
*
* @author Mark sunlightcs@gmail.com
*/
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Resource
private AuthorizationInterceptor authorizationInterceptor;
@Resource
private LoginUserHandlerMethodArgumentResolver loginUserHandlerMethodArgumentResolver;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor).addPathPatterns("/api/**");
}
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(loginUserHandlerMethodArgumentResolver);
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new StringHttpMessageConverter());
converters.add(new ResourceHttpMessageConverter());
converters.add(new AllEncompassingFormHttpMessageConverter());
converters.add(new StringHttpMessageConverter());
converters.add(jackson2HttpMessageConverter());
}
@Bean
public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
//日期格式转换
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN));
mapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
//Long类型转String类型
SimpleModule simpleModule = new SimpleModule();
simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
mapper.registerModule(simpleModule);
converter.setObjectMapper(mapper);
return converter;
}
}

View File

@@ -0,0 +1,60 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.controller;
import top.iletter.annotation.Login;
import top.iletter.common.utils.Result;
import top.iletter.common.validator.ValidatorUtils;
import top.iletter.dto.LoginDTO;
import top.iletter.service.TokenService;
import top.iletter.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
/**
* 登录接口
*
* @author Mark sunlightcs@gmail.com
*/
@RestController
@RequestMapping("/api")
@Tag(name = "登录接口")
@AllArgsConstructor
public class ApiLoginController {
private final UserService userService;
private final TokenService tokenService;
@PostMapping("login")
@Operation(summary = "登录")
public Result<Map<String, Object>> login(@RequestBody LoginDTO dto) {
//表单校验
ValidatorUtils.validateEntity(dto);
//用户登录
Map<String, Object> map = userService.login(dto);
return new Result().ok(map);
}
@Login
@PostMapping("logout")
@Operation(summary = "退出")
public Result logout(@Parameter(hidden = true) @RequestAttribute("userId") Long userId) {
tokenService.expireToken(userId);
return new Result();
}
}

View File

@@ -0,0 +1,54 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.controller;
import cn.hutool.crypto.digest.DigestUtil;
import top.iletter.common.utils.Result;
import top.iletter.common.validator.ValidatorUtils;
import top.iletter.dto.RegisterDTO;
import top.iletter.entity.UserEntity;
import top.iletter.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
/**
* 注册接口
*
* @author Mark sunlightcs@gmail.com
*/
@RestController
@RequestMapping("/api")
@Tag(name = "注册接口")
@AllArgsConstructor
public class ApiRegisterController {
private final UserService userService;
@PostMapping("register")
@Operation(summary = "注册")
public Result register(@RequestBody RegisterDTO dto) {
//表单校验
ValidatorUtils.validateEntity(dto);
UserEntity user = new UserEntity();
user.setMobile(dto.getMobile());
user.setUsername(dto.getMobile());
user.setPassword(DigestUtil.sha256Hex(dto.getPassword()));
user.setCreateDate(new Date());
userService.insert(user);
return new Result();
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.controller;
import top.iletter.annotation.Login;
import top.iletter.annotation.LoginUser;
import top.iletter.common.utils.Result;
import top.iletter.entity.UserEntity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 测试接口
*
* @author Mark sunlightcs@gmail.com
*/
@RestController
@RequestMapping("/api")
@Tag(name = "测试接口")
public class ApiTestController {
@Login
@GetMapping("userInfo")
@Operation(summary = "获取用户信息")
public Result<UserEntity> userInfo(@Parameter(hidden = true) @LoginUser UserEntity user) {
return new Result<UserEntity>().ok(user);
}
@Login
@GetMapping("userId")
@Operation(summary = "获取用户ID")
public Result<Long> userInfo(@Parameter(hidden = true) @RequestAttribute("userId") Long userId) {
return new Result<Long>().ok(userId);
}
@GetMapping("notToken")
@Operation(summary = "忽略Token验证测试")
public Result<String> notToken() {
return new Result<String>().ok("无需token也能访问。。。");
}
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.dao;
import top.iletter.common.dao.BaseDao;
import top.iletter.entity.TokenEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户Token
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface TokenDao extends BaseDao<TokenEntity> {
TokenEntity getByToken(String token);
TokenEntity getByUserId(Long userId);
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.dao;
import top.iletter.common.dao.BaseDao;
import top.iletter.entity.UserEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户
*
* @author Mark sunlightcs@gmail.com
*/
@Mapper
public interface UserDao extends BaseDao<UserEntity> {
UserEntity getUserByMobile(String mobile);
UserEntity getUserByUserId(Long userId);
}

View File

@@ -0,0 +1,33 @@
/**
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* 登录表单
*
* @author Mark sunlightcs@gmail.com
*/
@Data
@Schema(title = "登录表单")
public class LoginDTO {
@Schema(title = "手机号")
@NotBlank(message="手机号不能为空")
private String mobile;
@Schema(title = "密码")
@NotBlank(message="密码不能为空")
private String password;
}

View File

@@ -0,0 +1,32 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
/**
* 注册表单
*
* @author Mark sunlightcs@gmail.com
*/
@Data
@Schema(title = "注册表单")
public class RegisterDTO {
@Schema(title = "手机号")
@NotBlank(message="手机号不能为空")
private String mobile;
@Schema(title = "密码")
@NotBlank(message="密码不能为空")
private String password;
}

View File

@@ -0,0 +1,47 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户Token
*
* @author Mark sunlightcs@gmail.com
*/
@Data
@TableName("tb_token")
public class TokenEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private Long id;
/**
* 用户ID
*/
private Long userId;
/**
* 用户token
*/
private String token;
/**
* 过期时间
*/
private Date expireDate;
/**
* 更新时间
*/
private Date updateDate;
}

View File

@@ -0,0 +1,52 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.entity;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 用户
*
* @author Mark sunlightcs@gmail.com
*/
@Data
@TableName("tb_user")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 用户名
*/
private String username;
/**
* 手机号
*/
private String mobile;
/**
* 密码
*/
@JsonIgnore
private String password;
/**
* 创建时间
*/
private Date createDate;
}

View File

@@ -0,0 +1,55 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.exception;
import top.iletter.common.exception.ErrorCode;
import top.iletter.common.exception.RenException;
import top.iletter.common.utils.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* 异常处理器
*
* @author Mark sunlightcs@gmail.com
* @since 1.0.0
*/
@RestControllerAdvice
public class RenExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(RenExceptionHandler.class);
/**
* 处理自定义异常
*/
@ExceptionHandler(RenException.class)
public Result handleRenException(RenException ex){
Result result = new Result();
result.error(ex.getCode(), ex.getMsg());
return result;
}
@ExceptionHandler(DuplicateKeyException.class)
public Result handleDuplicateKeyException(DuplicateKeyException ex){
Result result = new Result();
result.error(ErrorCode.DB_RECORD_EXISTS);
return result;
}
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex){
logger.error(ex.getMessage(), ex);
return new Result().error();
}
}

View File

@@ -0,0 +1,72 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.interceptor;
import cn.hutool.core.util.StrUtil;
import top.iletter.annotation.Login;
import top.iletter.common.exception.ErrorCode;
import top.iletter.common.exception.RenException;
import top.iletter.entity.TokenEntity;
import top.iletter.service.TokenService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
/**
* 权限(Token)验证
*
* @author Mark sunlightcs@gmail.com
*/
@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
@Resource
private TokenService tokenService;
public static final String USER_KEY = "userId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Login annotation;
if (handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(Login.class);
} else {
return true;
}
if (annotation == null) {
return true;
}
//从header中获取token
String token = request.getHeader("token");
//如果header中不存在token则从参数中获取token
if (StrUtil.isBlank(token)) {
token = request.getParameter("token");
}
//token为空
if (StrUtil.isBlank(token)) {
throw new RenException(ErrorCode.TOKEN_NOT_EMPTY);
}
//查询token信息
TokenEntity tokenEntity = tokenService.getByToken(token);
if (tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()) {
throw new RenException(ErrorCode.TOKEN_INVALID);
}
//设置userId到request里后续根据userId获取用户信息
request.setAttribute(USER_KEY, tokenEntity.getUserId());
return true;
}
}

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.resolver;
import top.iletter.annotation.LoginUser;
import top.iletter.entity.UserEntity;
import top.iletter.interceptor.AuthorizationInterceptor;
import top.iletter.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
* 有@LoginUser注解的方法参数注入当前登录用户
*
* @author Mark sunlightcs@gmail.com
*/
@Component
@AllArgsConstructor
public class LoginUserHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
private final UserService userService;
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.getParameterType().isAssignableFrom(UserEntity.class) && parameter.hasParameterAnnotation(LoginUser.class);
}
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
//获取用户ID
Object object = request.getAttribute(AuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
if (object == null) {
return null;
}
//获取用户信息
UserEntity user = userService.getUserByUserId((Long) object);
return user;
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.service;
import top.iletter.common.service.BaseService;
import top.iletter.entity.TokenEntity;
/**
* 用户Token
*
* @author Mark sunlightcs@gmail.com
*/
public interface TokenService extends BaseService<TokenEntity> {
TokenEntity getByToken(String token);
/**
* 生成token
* @param userId 用户ID
* @return 返回token信息
*/
TokenEntity createToken(Long userId);
/**
* 设置token过期
* @param userId 用户ID
*/
void expireToken(Long userId);
}

View File

@@ -0,0 +1,34 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.service;
import top.iletter.common.service.BaseService;
import top.iletter.entity.UserEntity;
import top.iletter.dto.LoginDTO;
import java.util.Map;
/**
* 用户
*
* @author Mark sunlightcs@gmail.com
*/
public interface UserService extends BaseService<UserEntity> {
UserEntity getByMobile(String mobile);
UserEntity getUserByUserId(Long userId);
/**
* 用户登录
* @param dto 登录表单
* @return 返回登录信息
*/
Map<String, Object> login(LoginDTO dto);
}

View File

@@ -0,0 +1,98 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
*
* https://www.renren.io
*
* 版权所有,侵权必究!
*/
package top.iletter.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import top.iletter.common.service.impl.BaseServiceImpl;
import top.iletter.dao.TokenDao;
import top.iletter.entity.TokenEntity;
import top.iletter.service.TokenService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.UUID;
@Service
public class TokenServiceImpl extends BaseServiceImpl<TokenDao, TokenEntity> implements TokenService {
@Resource
private TokenDao tokenDao;
/**
* 12小时后过期
*/
private final static int EXPIRE = 3600 * 12;
@Override
public TokenEntity getByToken(String token) {
return baseDao.getByToken(token);
}
@Override
public TokenEntity createToken(Long userId) {
//当前时间
Date now = new Date();
//过期时间
Date expireTime = new Date(now.getTime() + EXPIRE * 1000);
//用户token
String token;
//判断是否生成过token
TokenEntity tokenEntity = baseDao.getByUserId(userId);
if(tokenEntity == null){
//生成一个token
token = generateToken();
tokenEntity = new TokenEntity();
tokenEntity.setUserId(userId);
tokenEntity.setToken(token);
tokenEntity.setUpdateDate(now);
tokenEntity.setExpireDate(expireTime);
//保存token
this.insert(tokenEntity);
}else{
//判断token是否过期
if(tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){
//token过期重新生成token
token = generateToken();
}else {
token = tokenEntity.getToken();
}
tokenEntity.setToken(token);
tokenEntity.setUpdateDate(now);
tokenEntity.setExpireDate(expireTime);
//更新token
this.updateById(tokenEntity);
}
return tokenEntity;
}
@Override
public void expireToken(Long userId){
Date now = new Date();
LambdaUpdateWrapper<TokenEntity> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(TokenEntity::getUserId, userId);
updateWrapper.set(TokenEntity::getExpireDate, now);
updateWrapper.set(TokenEntity::getUpdateDate, now);
tokenDao.update(updateWrapper);
}
private String generateToken(){
return UUID.randomUUID().toString().replace("-", "");
}
}

View File

@@ -0,0 +1,63 @@
/**
* Copyright (c) 2018 人人开源 All rights reserved.
* <p>
* https://www.renren.io
* <p>
* 版权所有,侵权必究!
*/
package top.iletter.service.impl;
import cn.hutool.crypto.digest.DigestUtil;
import top.iletter.common.exception.ErrorCode;
import top.iletter.common.exception.RenException;
import top.iletter.common.service.impl.BaseServiceImpl;
import top.iletter.common.validator.AssertUtils;
import top.iletter.dao.UserDao;
import top.iletter.dto.LoginDTO;
import top.iletter.entity.TokenEntity;
import top.iletter.entity.UserEntity;
import top.iletter.service.TokenService;
import top.iletter.service.UserService;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
@AllArgsConstructor
public class UserServiceImpl extends BaseServiceImpl<UserDao, UserEntity> implements UserService {
private final TokenService tokenService;
@Override
public UserEntity getByMobile(String mobile) {
return baseDao.getUserByMobile(mobile);
}
@Override
public UserEntity getUserByUserId(Long userId) {
return baseDao.getUserByUserId(userId);
}
@Override
public Map<String, Object> login(LoginDTO dto) {
UserEntity user = getByMobile(dto.getMobile());
AssertUtils.isNull(user, ErrorCode.ACCOUNT_PASSWORD_ERROR);
//密码错误
if (!user.getPassword().equals(DigestUtil.sha256Hex(dto.getPassword()))) {
throw new RenException(ErrorCode.ACCOUNT_PASSWORD_ERROR);
}
//获取登录token
TokenEntity tokenEntity = tokenService.createToken(user.getId());
Map<String, Object> map = new HashMap<>(2);
map.put("token", tokenEntity.getToken());
map.put("expire", tokenEntity.getExpireDate().getTime() - System.currentTimeMillis());
return map;
}
}

View File

@@ -0,0 +1,33 @@
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: renren
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
#Oracle需要打开注释
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true

View File

@@ -0,0 +1,33 @@
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: renren
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
#Oracle需要打开注释
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true

View File

@@ -0,0 +1,33 @@
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/renren_security?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true
username: renren
password: 123456
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
#Oracle需要打开注释
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true

View File

@@ -0,0 +1,83 @@
# Tomcat
server:
tomcat:
uri-encoding: UTF-8
threads:
max: 1000
min-spare: 30
port: 8081
servlet:
context-path: /renren-api
session:
cookie:
http-only: true
knife4j:
enable: true
basic:
enable: false
username: admin
password: admin
setting:
enableFooter: false
spring:
# 环境 dev|test|prod
profiles:
active: dev
messages:
encoding: UTF-8
basename: i18n/messages
# jackson时间格式化
jackson:
time-zone: GMT+8
date-format: yyyy-MM-dd HH:mm:ss
mvc:
pathmatch:
matching-strategy: ANT_PATH_MATCHER
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
enabled: true
data:
redis:
database: 0
host: 192.168.10.10
port: 6379
password: # 密码(默认为空)
timeout: 6000ms # 连接超时时长(毫秒)
lettuce:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接
renren:
redis:
open: false # 是否开启redis缓存 true开启 false关闭
#mybatis
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
#实体扫描多个package用逗号或者分号分隔
typeAliasesPackage: io.renren.entity
global-config:
#数据库相关配置
db-config:
#主键类型
id-type: ASSIGN_ID
banner: false
#原生配置
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
call-setters-on-nulls: true
jdbc-type-for-null: 'null'
configuration-properties:
prefix:
blobType: BLOB
boolValue: TRUE

View File

@@ -0,0 +1,5 @@
====================================================================================================================
欢迎使用 renren-api - Powered By https://www.renren.io
====================================================================================================================

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springboot.sample" level="TRACE" />
<!-- 开发、测试环境 -->
<springProfile name="dev,test">
<logger name="org.springframework.web" level="INFO"/>
<logger name="org.springboot.sample" level="INFO" />
<logger name="io.renren" level="DEBUG" />
</springProfile>
<!-- 生产环境 -->
<springProfile name="prod">
<logger name="org.springframework.web" level="ERROR"/>
<logger name="org.springboot.sample" level="ERROR" />
<logger name="io.renren" level="ERROR" />
</springProfile>
</configuration>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.renren.dao.TokenDao">
<select id="getByToken" resultType="top.iletter.entity.TokenEntity">
select * from tb_token where token = #{value}
</select>
<select id="getByUserId" resultType="top.iletter.entity.TokenEntity">
select * from tb_token where user_id = #{value}
</select>
</mapper>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.renren.dao.UserDao">
<select id="getUserByMobile" resultType="top.iletter.entity.UserEntity">
select * from tb_user where mobile = #{value}
</select>
<select id="getUserByUserId" resultType="top.iletter.entity.UserEntity">
select * from tb_user where id = #{value}
</select>
</mapper>