SpringBoot+Vue前后端分离管理系统03:后端

该文详细介绍了如何基于SpringBoot2.7.8搭建后端项目,包括引入Web、数据库、MyBatis-Plus、Lombok等依赖,配置数据库连接,使用MyBatisPlus代码生成工具,创建登录接口并集成Redis进行Token管理,以及用户信息获取和注销接口。同时,文章也涉及到了前后端跨域问题的解决方案和用户管理页面的接口开发。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

后端项目初始化 

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代码生成

官网:代码生成器(新) | MyBatis-Plus

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
methodpost
请求参数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
methodget
请求参数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
methodpost
请求参数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("删除用户成功");
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值