后端职责可以粗浅的理解为处理各种数据,那么处理数据就可以从下面几个方面考虑:
数据的来源
根据不同的数据来源,我们探究两个方面的内容:
数据的形式
数据的操作
当然,一通操作以后,各个来源的数据需要通信,所以还有一个:数据的交互
数据来源
想要持久化存储数据,需要将数据存入数据库。
想要对数据进行各种丰富的处理,需要将数据存放进 Java 的各种数据结构中(List、Map…)。
还有一个来源就是用户在前端的输入。
1. 数据库
数据形式: 关系型数据库里面的存储可以见到的组织形式就是表。
数据操作: SQL 语言
2. Java 后端
数据形式: 类,数据结构
数据操作: 方法调用
3. 前端
数据形式: 字符串,JSON
数据操作: JavaScript
数据的交互
数据的来源有三个,一般都是以 Java 后端作为中间桥梁。我们一对一对的看。数据交互要达到的效果就是统一形式、互相操作。
数据库与 Java 后端:
数据库一般只是用来提供数据,所以交互的任务就落到了 Java 后端的身上。
数据库里面数据组织的形式是表,Java 里面是类,因为我们要把数据拿到 Java 里面去操作。一个很自然的想法就是,将表直接映射成 Java 里面的类,由于类只是一个结构,我们具体操作的是类的对象。所以我们更多地会说成将表映射成对象。由于这里的表特指关系型数据库的表,所以这种映射被称为 Object–relational mapping,即对象关系映射,简称 ORM.
那么表能变成对象吗?我们发现是可以的。因为表里的各个属性可以映射到对象的属性上来,每一条表的记录都可以映射成一个对象的各个属性
理论上是可行的,那实际操作呢?Java 要想将表的记录转变成对象,那就只能通过一系列方法的执行来做到了。
Java 程序员甚至不需要懂 SQL,就完成了和数据库的交互。可以概括如下:
定义实体类
写接口
调用接口
但是默认的这些方法所能够拥有的功能是有限的,对于国内的互联网公司来说,业务可能会十分复杂,数据访问量也会比较大,这时候良好的 SQL 可能就是必要的啦。虽然说 Spring Data JPA 支持自定义 SQL,但是这不是它的长处,在某些情境下是要受到限制的。于是虽然世界上整体流行使用 JPA,但是我们国内用的不多。
于是我们需要一种对 SQL 支持良好的框架,程序员的注意力可以从写 Java 代码转到了写 SQL 上。这款框架就是 MyBatis.
那么 MyBatis 的步骤就是多了几步:
定义实体类
写接口
写 SQL
接口与 SQL 绑定
调用接口
在 MyBatis 官方文档里有这样几句话:
MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
MyBatis Plus. 它在 MyBatis 的基础上增加了一些常用的接口和功能。并且还有一个神奇的代码生成器,只需要在图形界面点两下,代码和映射文件都会帮你生成好,连代码都不需要写了。
后端对数据的处理
不管你是用什么方式从数据库拿到的数据,在 Java 里面就是一个个对象,这时候就可以拿 Java 语言对数据进行各种操作咯。那么处理完成以后,你还需要返回给前端
前后端数据交互
既然要返回给前端,那么数据形式仍然要统一。前端可以认识的形式就是 JSON,所以我们需要把 Java 对象转换成 JSON,数据格式如果统一的话,那么我们还要想的就是前后端如何通信。
前端每一个请求都对应一个URL,我们将 URL 和 Java 的方法建立映射,一旦在前端访问到 URL,我们就执行相应的后端方法处理相应的请求。
而这些工作在 Spring Boot 里面都有了很好的支持,我们通常使用注解来进行实现。
当然前端也可以发送 JSON 格式的数据给后端,后端转换为 Java 对象,再通过 ORM 框架完成存入数据库的操作。
后端分层的依据
我们可以看到,Java 后端承载着太多的使命:
数据库访问
数据处理
前后端交互
那么将每一项使命放在不同的包里,就对应的变成了 dao 层、service 层和 controller 层。
应用模型:
springboot 的角色是用来处理用户请求的,从client端向springboot端发送请求,然后向数据库请求数据,数据库返回数据给前端
1 idea连接mysql
点击右边的数据库 -> + -> 数据源-> MySQL,输入账号、密码、数据库名称,这里是kob,点击测试连接,成功后点击应用就可以了。
创建数据库 kill9
create database kill9;
2 配置springboot
2.1添加依赖和数据库配置
添加依赖
在pom.xml下添加依赖,依赖可以 Maven仓库地址 中寻找。
Spring Boot Starter JDBC
Project Lombok
MySQL Connector/J
mybatis-plus-boot-starter
mybatis-plus-generator
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
选择哪个版本号,都可以,个人习惯是最新的几个版本里选用的人最多的版本。
点击 maven 的重新加载,刷新 Maven。
在application.properties中添加数据库配置:
//输入你自己的用户和密码
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/kob?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
点击运行 出现报错 可能是因为路径问题。
点击运行,输入网址 http://127.0.0.1:8080/pk/index/ 显示界面就成功了
2.2实现简单的crud
SpringBoot中的常用模块:
pojo层:将数据库中的表对应成Java中的Class
mapper层(也叫Dao层):将pojo层的class中的操作,映射成sql语句
service层:写具体的业务逻辑,组合使用mapper中的操作
controller层:负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面
注解:
使用注解可以帮助我们不在需要配置繁杂的xml文件,以前最基本的web项目是需要写xml配置的,需要标注你的哪个页面和哪个 servle 是对应的,注解可以帮助我们减少这方面的麻烦。
@Controller:用于定义控制器类,在spring项目中由控制器负责将用户发来的URL请求转发到对应的服务接口(service层),一般这个注解在类中,通常方法需要配合注解@RequestMapping。
@RequestMapping:提供路由信息,负责URL到Controller中的具体函数的映射。
@Autowired:自动导入依赖的bean
@Service:一般用于修饰service层的组件
@Bean:用@Bean标注方法等价于XML中配置的bean。
@AutoWired:自动导入依赖的bean。byType方式。把配置好的Bean拿来用,完成属性、方法的组装,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。当加上(required=false)时,就算找不到bean也不报错。
在 backend 下创建 pojo 包 创建一个类 User,将数据库中的表 User转化为 Java 中的 User.class
package com.kill9.backend.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String username;
private String password;
}
在backend创建mapper 包,创建一个 Java 类的接口 UserMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
在backend 的 controller 下创建 user 包然后创建 UserController.
package com.kill9.backend.controller.user;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kill9.backend.mapper.UserMapper;
import com.kill9.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class UserController {
@Autowired
UserMapper userMapper;
/**
* 查询所有用户
*/
@GetMapping("/user/all/")
public List<User> getAll(){
return userMapper.selectList(null);
}
/**
* 查询单个用户
*/
@GetMapping("/user/{userId}/")
public User getUser(@PathVariable int userId){
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id",userId);
return userMapper.selectOne(queryWrapper);
// 范围遍历
// public List<User> getUser(int userId)
// queryWrapper.ge("id", 2).le("id", 3);
// return userMapper.selectList(queryWrapper);
}
/**
* 添加某个用户 直接输入 id name password
* @param userId
* @param username
* @param password
* @return Add User Sucessfully
*/
@GetMapping("/user/add/{userId}/{username}/")
public String addUser(@PathVariable int userId,
@PathVariable String username,
@PathVariable String password){
User user = new User(userId,username,password);
userMapper.insert(user);
return "Add User Successfully";
}
/**
* 删除某个用户,直接输入 id
* @param userId
* @return Delete User Successfully
*/
@GetMapping("/user/delete/{userId}/")
public String deleteUser(@PathVariable int userId) {
userMapper.deleteById(userId);
return "Delete User Successfully";
}
}
查询user中的全部数据。
查询user中的单个数据。
添加 user 中的数据。
删除 user 中的数据。
3 配置spring security
是用户认证操作 – 一种授权机制,目的是安全。
原理解释
数据库可以知道sessionId对应的用户是谁
如果想让我们的security对接我们的数据库
需要把username在数据库中对应的的用户找出来,返回他的密码
需要用到数据库操作mapper,能写private就写private,用数据库记得加上autowired
3.1添加依赖
1. 添加依赖,添加之后刷新。
spring-boot-starter-security
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.7.0</version>
</dependency>
刷新之后显示登陆:
默认的叫 Username 是 user ,密码自动生成。
3.2与数据库对接
在backend 的 service 创建 impl 包,新建 UserDetailsServiceImpl 类。
实现service.impl.UserDetailsServiceImpl类,继承自UserDetailsService接口,用来接入数据库信息。
package com.kill9.backend.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.kill9.backend.mapper.UserMapper;
import com.kill9.backend.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",username);
User user = userMapper.selectOne(queryWrapper);
if(user==null){
throw new RuntimeException("用户不存在");
}
return new UserDetailsImpl(user);
}
}
在backend 的 service 包的 impl 包下创建utils 包 新建 UserDetailsImpl。
package com.kill9.backend.service.impl.utils;
import com.kill9.backend.pojo.User;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDetailsImpl implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
3.3测试密文
如果实现登录的话,需要提供一个 PassworEncoder。
如果在数据库中指定明文来存储,需要在自己的密码加上{noop},才可以登录。
在 Test 下生成需要转换的密文,同时修改数据库下的密码为密文
package com.kill9.backend;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@SpringBootTest
class BackendApplicationTests {
@Test
void contextLoads() {
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
System.out.println(passwordEncoder.encode("123456"));
System.out.println(passwordEncoder.encode("111111"));
}
}
重启项目,进入login页面
登录成功,只要不给你显示密码错误就代表成功,显示Whitelabel Error Page也是成功,自己在后面添加路径就可以了登录。
1. 实现密文存储:
上传之后就能登录,数据库知道没有加密之后就不会用到PasswordEncoder
实现任何用户都可以登录在 config 下新建 SecurityConfig 。
实现config.SecurityConfig类,用来实现用户密码的加密存储。加密算法
如果不明文我们可以,将左边哈希到右边,就算数据库泄露我们也不会泄露用户密码
![]()
package com.kill9.backend.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
使用密文添加用户 :
修改 controller 下的 user 的 UserController的注册,密码直接存储加密之后的密码。
@GetMapping("/user/add/{userId}/{username}/{password}/")
public String addUser(@PathVariable int userId,
@PathVariable String username,
@PathVariable String password){
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodePassword = passwordEncoder.encode(password);
User user = new User(userId,username,encodePassword);
userMapper.insert(user);
return "Add User Successfully";
}
完成~