「完整FX」SpringBoot2 仿B站高性能前端+后端项目

112 阅读6分钟

SpringBoot2 仿B站高性能前端+后端项目

xia讠果☛ lexuecode.com/7338.html

搭建数据库与持久层框架

1. 需求分析与规划

  • 业务需求:明确应用程序需要存储哪些数据,以及数据之间的关系。
  • 性能需求:评估数据的读写频率、并发量和预期的负载。
  • 数据一致性:确定数据一致性和完整性的要求。
  • 可扩展性:考虑未来数据增长和系统扩展的需求。

2. 数据库选择

  • 关系型数据库:如MySQL、PostgreSQL、Oracle等,适用于结构化数据和复杂关系。
  • 非关系型数据库:如MongoDB、Cassandra、Redis等,适用于非结构化数据或需要水平扩展的场景。

3. 数据库设计

  • 数据模型:根据业务需求设计数据模型,包括表结构、字段、数据类型和索引。
  • 关系设计:对于关系型数据库,设计表之间的关系,如一对一、一对多、多对多。
  • 规范化:确保数据模型满足数据库规范化原则,减少数据冗余。

4. 数据库配置与部署

  • 安装数据库:根据操作系统和环境安装合适的数据库软件。
  • 配置数据库:设置数据库的连接参数、字符集、存储引擎等。
  • 安全性:配置用户权限、加密连接等安全措施。

5. 持久层框架选择

  • ORM(对象关系映射)框架:如Hibernate、Entity Framework、Django ORM等,将对象模型映射到关系模型。
  • 数据访问框架:如MyBatis、JdbcTemplate等,提供数据访问的抽象和简化。

6. 持久层框架集成

  • 配置数据源:在应用程序中配置数据库连接池和数据源。
  • 实体类映射:定义实体类与数据库表的映射关系。
  • SQL映射:编写或配置SQL语句,定义数据的查询、插入、更新和删除操作。

7. 事务管理

  • 事务控制:确保数据操作的原子性、一致性、隔离性和持久性。
  • 声明式事务:在ORM框架中使用注解或XML配置事务边界。

8. 测试与优化

  • 单元测试:编写单元测试来验证数据访问逻辑的正确性。
  • 性能测试:进行性能测试,优化SQL语句和索引。
  • 监控:监控数据库性能和资源使用情况。

9. 部署与维护

  • 备份与恢复:制定数据库备份策略,确保数据安全。
  • 版本控制:随着应用的迭代,管理数据库架构的变更。
  • 监控与调优:持续监控数据库性能,根据需要进行调优。

SpringBoot2 基于JWT的用户token验证

JWT的结构 JWT由三段信息用.连接构成的字符串。

Header.Payload.Signature 例如:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxIn0.ihOZFzg3ZGIbBMneRy-4RMqors1P3nuO-wRJnQtTzWQ
  • Header头部

承载两部分信息:token类型和采用的加密算法

{ 
  "typ": "JWT",
  "alg": "HS256"
}

token类型:JWT 加密算法:HS256

  • Payload负载

存放有效信息的地方

iss: jwt签发者 sub: jwt所面向的用户 aud: 接收jwt的一方 exp: 过期时间戳(jwt的过期时间,这个过期时间必须要大于签发时间) nbf: 定义在什么时间之前,该jwt都是不可用的 iat: jwt的签发时间 jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

  • Signature签名

对头部及负载内容进行签证。采用Header中声明的算法,接收三个参数:base64编码的Header、base64编码的Payload和密钥(secret)进行运算。密钥secret是保存在服务端的,服务端会根据这个密钥进行生成token和进行验证。

  1. 输入用户名和密码,进行登录 2. 服务器对登录用户进行认证(如果认证通过,根据用户的信息和JWT的生成规则生成token) 3. 服务器将该token字符串返回给用户 4. 当用户请求服务器API时,在请求的Header中加入token 5. 服务端进行校验(如果通过,则解析其中内容,根据其权限和业务逻辑给出响应结果。如果不通过,返回401) 6. 返回请求数据

SpringBoot集成JWT实现token验证步骤

1. 引入JWT依赖

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.4.0</version>
</dependency>

2. 自定义实体User类

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    String Id;
    String username;
    String password;
}

3. 生成token方法

@Service("TokenService")
public class TokenService {
    public String getToken(User user) {
        String token="";
        // 存入需要保存在token里的信息,这里把用户ID存入token
        token= JWT.create().withAudience(user.getId())
                .sign(Algorithm.HMAC256(user.getPassword()));
        // 使用HMAC256加密算法生成token,密钥是用户的密码
        return token;
    }
}

4. 拦截器获取并验证token

public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    UserService userService;
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        // 从 http 请求头中取出 token
        String token = httpServletRequest.getHeader("token");
        // 如果不是映射到方法直接通过
        if(!(object instanceof HandlerMethod)){
            return true;
        }
        HandlerMethod handlerMethod=(HandlerMethod)object;
        Method method=handlerMethod.getMethod();
        // 检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        // 检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(UserLoginToken.class)) {
            UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new RuntimeException("无token,请重新登录");
                }
                // 获取 token 中的 userId
                String userId;
                try {
                    userId = JWT.decode(token).getAudience().get(0);
                } catch (JWTDecodeException j) {
                    throw new RuntimeException("401");
                }
                User user = userService.findUserById(userId);
                if (user == null) {
                    throw new RuntimeException("用户不存在,请重新登录");
                }
                // 验证 token
                JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                try {
                    jwtVerifier.verify(token);
                } catch (JWTVerificationException e) {
                    throw new RuntimeException("401");
                }
                return true;
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }
}

boolean preHandle()预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller 返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行postHandle()和afterCompletion() 返回值为false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行

5. 配置拦截器 在配置类上添加了注解@Configuration,标明了该类是一个配置类并且会将该类作为一个SpringBean添加到IOC容器内

@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    //addPathPatterns方法用于设置拦截器的过滤路径规则
    public void addInterceptors(InterceptorRegistry registry) {
        // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

6. 数据访问接口

@RestController
@RequestMapping("api")
public class UserApi {
    @Autowired
    UserService userService;
    @Autowired
    TokenService tokenService;
    //登录
    @PostMapping("/login")
    public Object login( User user){
        JSONObject jsonObject=new JSONObject();
        User userForBase=userService.findByUsername(user);
        if(userForBase==null){
            jsonObject.put("message","登录失败,用户不存在");
            return jsonObject;
        }else {
            if (!userForBase.getPassword().equals(user.getPassword())){
                jsonObject.put("message","登录失败,密码错误");
                return jsonObject;
            }else {
                String token = tokenService.getToken(userForBase);
                jsonObject.put("token", token);
                jsonObject.put("user", userForBase);
                return jsonObject;
            }
        }
    }
    
    @UserLoginToken
    @GetMapping("/getMessage")
    //登录注解,说明该接口必须登录获取token后,在请求头中加上token并通过验证才可以访问
    public String getMessage(){
        return "你已通过验证";
    }
}