后端项目初始化
1、创建一个springboot 2.7.8项目
2、导入依赖
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.2</version>
</dependency>
<!-- freemarker -->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
3、编写application.yaml
server:
port: 9999
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/xdm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC
logging:
level:
com: debug
MyBatisPlus代码生成
1、在test/java/com包下,编写代码生成器
public class CodeGenerator {
public static void main(String[] args){
String url = "jdbc:mysql://localhost:3306/xdm?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC";
String username = "root";
String password = "666666QIU";
String moduleName = "sys";
String mapperLocation = "src\\main\\resources\\mapper\\" + moduleName;
String tables = "x_menu,x_role,x_role_menu,x_user,x_user_role";
FastAutoGenerator.create(url, username, password)
.globalConfig(builder -> {
builder.author("丘桔") // 设置作者
.outputDir("src\\main\\java"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com") // 设置父包名
.moduleName(moduleName) // 设置父包模块名
.pathInfo(Collections.singletonMap(OutputFile.xml, mapperLocation)); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude(tables) // 设置需要生成的表名
.addTablePrefix("x_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
2、给启动类加注解
@MapperScan("com.*.mapper")
公共响应类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {
private Integer code;
private String message;
private T data;
public static <T>Result<T> success(){
return new Result<>(20000,"success",null);
}
public static <T>Result<T> success(String message){
return new Result<>(20000,message,null);
}
public static <T>Result<T> success(T data){
return new Result<>(20000,"success",data);
}
public static <T>Result<T> success(String message, T data){
return new Result<>(20000,message,data);
}
public static <T>Result<T> fail(){
return new Result<>(20001,"fail",null);
}
public static <T>Result<T> fail(Integer code, String message){
return new Result<>(code,message,null);
}
public static <T>Result<T> fail(String message){
return new Result<>(20001,message,null);
}
public static <T>Result<T> fail(T data){
return new Result<>(20001,"fail",data);
}
public static <T>Result<T> fail(String message, T data){
return new Result<>(20001,message,data);
}
}
登录接口
接口属性 | 值 |
---|---|
url | /user/login |
method | post |
请求参数 | username password |
返回参数 | { "code": 20000, "message": "success", "data": { "token": "user:71957608-08d0-4e29-b875-a497763f1db8" } } |
要做的事:用户访问/user/login请求的时候,判断用户名密码是否正确,如果正确,给前端返回token,并且将token和uuid存入redis中
1、导入redis依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.2</version>
</dependency>
2、在application.yaml中,配置redis
spring:
redis:
host: localhost
port: 6379
3、编写redis配置类
@Configuration
public class MyRedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate redisTemplate(){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
redisTemplate.setValueSerializer(serializer);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
om.setTimeZone(TimeZone.getDefault());
om.configure(MapperFeature.USE_ANNOTATIONS, false);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
serializer.setObjectMapper(om);
return redisTemplate;
}
}
4、编写service层
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Autowired
private RedisTemplate redisTemplate;
public Map<String, Object> login(User user) {
//根据用户名和密码查询
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, user.getUsername());
wrapper.eq(User::getPassword, user.getPassword());
User loginUser = this.baseMapper.selectOne(wrapper);
//结果不为空,则生成token,并将用户信息存入redis
if(loginUser != null){
//暂时用UUID,终极方案是jwt
String key = "user:" + UUID.randomUUID();
//存入redis
loginUser.setPassword(null); //密码不用存到redis中
//存入key value,并且设置过期时间为30分钟
redisTemplate.opsForValue().set(key,loginUser,30, TimeUnit.MINUTES);
//返回数据
HashMap<String, Object> data = new HashMap<>();
data.put("token",key);
return data;
}else {
return null;
}
}
}
5、编写controller层
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserServiceImpl userService;
@PostMapping("/login")
public Result<Map<String, Object>> login(@RequestBody User user){
Map<String,Object> data = userService.login(user);
if(data != null){
return Result.success(data);
}else {
return Result.fail(20002,"用户名或密码错误");
}
}
}
获取用户信息
接口属性 | 值 |
---|---|
url | /user/info?token=xxx |
method | get |
请求参数 | token |
返回参数 | { "code": 20000, "message": "success", "data": { "roles": [ "admin", "hr" ], "name": "admin", "avatar": "https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif" } } |
1、导入依赖
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.7</version>
</dependency>
2、编写UserMapper接口
public interface UserMapper extends BaseMapper<User> {
public List<String> getRoleNameByUserId(Integer userId);
}
3、编写UserMapper.xml
<select id="getRoleNameByUserId" parameterType="Integer" resultType="String">
select r.role_name
from x_user_role ur
left join x_role r
on ur.id = r.role_id
where ur.user_id = #{user_id};
</select>
4、编写Service层
@Override
public Map<String, Object> getUserInfo(String token) {
//根据token,从redis中获取用户信息
Object obj = redisTemplate.opsForValue().get(token);
if(obj != null){
User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class);
Map<String, Object> data = new HashMap<>();
data.put("name", loginUser.getUsername());
data.put("avatar", loginUser.getAvatar());
//角色
List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId());
data.put("roles",roleList);
return data;
}else {
return null;
}
}
5、编写Controller层
@GetMapping("/info")
public Result<Map<String, Object>> getUserInfo(@RequestParam("token") String token){
//根据token获取用户信息
Map<String, Object> data = userService.getUserInfo(token);
if(data != null){
return Result.success(data);
}else {
return Result.fail(20003,"登录信息无效,请重新登录");
}
}
注销接口
接口属性 | 值 |
---|---|
url | /user/logout |
method | post |
请求参数 | X-Token(在Request Header中) |
返回参数 | { "code": 20000, "message": "success", "data": null } |
注销直接在redis中,把token删掉即可
1、编写service层
@Override
public void logout(String token) {
redisTemplate.delete(token);
}
2、编写controller层
@PostMapping("/logout")
public Result<?> logout(@RequestHeader("X-Token") String token){
userService.logout(token);
return Result.success();
}
前后端对接
1、更改src/api/user.js下的路径
2、更改 .env.development下的路径
3、删掉vue.config.js的第39行
4、运行之后会报错
原因:跨域(ip地址或者端口号不同),前端是localhost:8888,后端是localhost:9999
5、解决跨域问题
方法一:在每一个controller上加 @CrossOrigin 注解
方法二:写一个配置类,去做全局的跨域处理
@Configuration
public class MyCorsConfig {
@Bean
public CorsFilter corsFilter(){
CorsConfiguration configuration = new CorsConfiguration();
//允许的ip和端口,不能写*,不安全,并且cookie无法使用
configuration.addAllowedOrigin("http://localhost:8888"); //允许这个localhost这个ip,8888这个端口访问后端
configuration.setAllowCredentials(true); //允许给后端传cookie
configuration.addAllowedMethod("*"); //允许所有的请求方式访问后端
configuration.addAllowedHeader("*"); //允许所有的请求头
//添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**",configuration);
return new CorsFilter(source);
}
}
用户管理页面布局
参考我之前写的前端部分:SpringBoot+Vue前后端分离管理系统02:前端_丘桔的博客-优快云博客
用户列表查询接口
1、编写MyBatisPlus分页插件配置
@Configuration
public class MyPageConfig {
/**
* 新的分页插件,一缓和二缓遵循mybatis的规则,需要设置 MybatisConfiguration#useDeprecatedExecutor = false 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
2、编写controller
@GetMapping("/list")
public Result<Map<String, Object>> getUserList(@RequestParam(value = "username", required = false) String username,
@RequestParam(value = "phone", required = false) String phone,
@RequestParam(value = "pageNo") Integer pageNo,
@RequestParam(value = "pageSize") Integer pageSize){
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(StringUtils.hasLength(username), User::getUsername,username);
wrapper.eq(StringUtils.hasLength(phone), User::getPhone, phone);
Page<User> page = new Page<>(pageNo, pageSize);
userService.page(page,wrapper);
Map<String, Object> data = new HashMap<>();
data.put("total", page.getTotal());
data.put("rows", page.getRecords());
return Result.success(data);
}
用户列表查询对接后端
1、在src/api目录下,创建userManager.js
import request from '@/utils/request'
export default {
getUserList(searchModel){
return request({
url: '/user/list',
method: 'get',
params: {
pageNo: searchModel.pageNo,
pageSize: searchModel.pageSize,
username: searchModel.username,
phone: searchModel.phone
}
})
}
}
2、在src/views/sys/user.vue中,编写方法
methods: {
handleSizeChange(pageSize){
this.searchModel.pageSize = pageSize
this.getUserList()
},
handleCurrentChange(pageNo){
this.searchModel.pageNo = pageNo
this.getUserList()
},
getUserList(){
userApi.getUserList(this.searchModel).then(reponse => {
this.userList = reponse.data.rows
this.total = reponse.data.total
})
}
},
created() {
this.getUserList()
}
表单提交
@PostMapping("/addUser")
public Result<?> addUser(@RequestBody User user){
userService.save(user);
return Result.success("新增用户成功");
}
密码加密
1、导入依赖
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
2、注册bean
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
3、增加用户的时候对密码加密
@Autowired
private PasswordEncoder passwordEncoder;
@PostMapping("/addUser")
public Result<?> addUser(@RequestBody User user){
user.setPassword(passwordEncoder.encode(user.getPassword()));
userService.save(user);
return Result.success("新增用户成功");
}
4、修改登录逻辑
新增用户
@PutMapping("/updateUser")
public Result<?> updateUser(@RequestBody User user){
user.setPassword(null);
userService.updateById(user); //这个方法中,如果某个字段值为空,那么该字段的值不会更新
return Result.success("修改用户成功");
}
@GetMapping("/{id}")
public Result<?> getUserById(@PathVariable("id") Integer id){
User user = userService.getById(id);
return Result.success(user);
}
删除用户
1、配置application.yaml
mybatis-plus:
global-config:
db-config:
logic-delete-field: deleted # 全局逻辑删除的实体字段名(since 3.3.0,配置后可以忽略不配置步骤2)
logic-delete-value: 1 # 逻辑已删除值(默认为 1)
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
2、编写controller
@DeleteMapping("/{id}")
public Result<?> DeleteById(@PathVariable("id") Integer id){
userService.removeById(id);
return Result.success("删除用户成功");
}