首页 > 其他分享 >自用的springboot后端轮子

自用的springboot后端轮子

时间:2022-12-23 23:22:40浏览次数:66  
标签:return springboot private token 自用 Result 轮子 public String

Springboot后端简易方式快速搭建

前言

快速学了下。和传统的springboot项目相比,没有用service和serviceImpl。比较不合规,但够简单。可以用于快速开发。

前后端分离。前端请另寻。

特别感谢:程序员青戈

程序员青戈的个人空间_哔哩哔哩_bilibili

1 开始

2 手动导入一些依赖

<!--mysql驱动-->
		<dependency>
   			 <groupId>mysql</groupId>
   			 <artifactId>mysql-connector-java</artifactId>
   			 <scope>runtime</scope>
		</dependency>
<!--lombook 哦对 在项目创建就可以导入了-->
		<dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--mybatis-plus 解放增删改查-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.1</version>
        </dependency>
 <!--  JWT Token相关 -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
<!--  超多好用的工具 强烈推荐 -->
 		<dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.7.3</version>
        </dependency>
<!--  加密工具 -->
		<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

3 建个数据库

4 application.properties

# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/springboot-vue?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=123456
# 应用服务 WEB 访问端口
server.port=9090

5 返回Result

public class Result<T> {
    private String code;
    private String msg;
    private T data;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Result() {
    }

    public Result(T data) {
        this.data = data;
    }

    public static Result success() {
        Result result = new Result<>();
        result.setCode("0");
        result.setMsg("成功");
        return result;
    }

    public static <T> Result<T> success(T data) {
        Result<T> result = new Result<>(data);
        result.setCode("0");
        result.setMsg("成功");
        return result;
    }

    public static Result error(String code, String msg) {
        Result result = new Result();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
}

6 文件结构预览

7 entity.User

这里写实体。属性和表一一对应

注意看注释!!

@TableName("user")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String username;
    private String password;
    private String nickName;
    private Integer age;
    private String sex;
    private String address;
//    private String avatar;
//    @TableField(exist = false)
//    private List<Integer> roles;
//
//    @TableField(exist = false)
//    private List<Book> bookList;
//
//    @TableField(exist = false)
//    private String token;
//
//    private BigDecimal account;
//
//    @TableField(exist = false)
//    private Set<Permission> permissions;
}

8 mapper.UserMapper

这个BaseMapper自带增删改查。如果有其他需求可以自己写。

public interface UserMapper  extends BaseMapper<User> {
}

9 controller.UserController

可以了解一下restful风格接口。

@RestController
@RequestMapping("/user")
public class UserController {
    @Resource
    UserMapper userMapper;

    @PostMapping
    public Result<?> save(@RequestBody User user){
        userMapper.insert(user);
        return Result.success();

    }
}

10 体验一下

在上面的controller输入:

@GetMapping("/all")
public Result<?> findAll() {
    return Result.success(userMapper.selectList(null));
}

然后访问127.0.0.1:9090/user/all

就能看到数据库的json啦 是不是很简单呢

返回的json交给前端耍了。

(一定要找一个好前端啊 就算后端写的比较拉也能高逼格)

以下是写完项目后的一些记录,包含一些重要的配置和代码。

11.一些配置

11.1 允许客户端携带验证信息 AddResponseHeaderFilter

因为本项目将使用cookie,因此必须允许客户端携带验证信息。实现的方法是继承重写spring web的OncePerRequestFilter,对每一个申请都在回复中添加请求头Access-Control-Allow-Credentials为ture。

@Component
public class AddResponseHeaderFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        response.addHeader("Access-Control-Allow-Credentials", "true");
        filterChain.doFilter(request, response);
    }
}

11.2 跨域设置 CorsConfig

前后端分离的跨域设置。允许前端的origin向后端发送请求。

@Configuration
public class CorsConfig {

    // 当前跨域请求最大有效时长。这里默认1天
    private static final long MAX_AGE = 24 * 60 * 60;

    private CorsConfiguration buildConfig() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedOrigin("http://127.0.0.1:前端端口"); // 1 设置访问源地址
        corsConfiguration.addAllowedOrigin("https://前端.com");
        corsConfiguration.addAllowedHeader("*"); // 2 设置访问源请求头
        corsConfiguration.addAllowedMethod("*"); // 3 设置访问源请求方法
        corsConfiguration.setMaxAge(MAX_AGE);
        return corsConfiguration;
}
        @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", buildConfig()); // 4 对接口配置跨域设置
        return new CorsFilter(source);
    }
}

11.3 MybatisPlus配置 MybatisPlusConfig

该处主要配置了分页插件。后续的博客搜索、评论搜索等都会用到分页查询

@Configuration
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

11.4 其他配置

在application.java中的配置:引入了spring security的加密。

@SpringBootApplication(exclude= SecurityAutoConfiguration.class)
@MapperScan("com.example.QLblog.mapper")
public class SpringbootTestApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringbootTestApplication.class, args);
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }

}

我们只要用:

bCryptPasswordEncoder.encode(user.getPassword())

即可加密密码。当然,控制类需装配进来:

@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;

在application.properties中配置:(隐去了敏感信息)

# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# 数据库用户名&密码:
spring.datasource.username=***

#开发版
#spring.datasource.url=jdbc:mysql://***:3306/qlblog?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
#spring.datasource.password=***

#发行版
spring.datasource.url=jdbc:mysql://localhost:3306/qlblog?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8
spring.datasource.password=***
# 应用服务 WEB 访问端口
server.port=9090

# 文件上传ip
file.ip=***

#文件上传限额
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB

#连接sql字符集为utf8mb4 可能是多余的 
spring.datasource.hikari.connection-init-sql=set names utf8mb4 collate utf8mb4_unicode_ci
spring.datasource.tomcat.init-s-q-l=set names utf8mb4 collate utf8mb4_unicode_ci

12 Cookie相关

12.1 生成Token的工具

之后生成cookie时,会先将用户信息装进token,再装进cookie。本工具类定义了通过user生成token的方法。

@Slf4j
@Component
public class TokenUtils {

    @Resource
    private UserMapper userMapper;

    private static UserMapper staticUserMapper;

    @PostConstruct
    public void init() {
        staticUserMapper = userMapper;
    }

    /**
     * 生成token
     * @param user
     * @return
     */
    public static String genToken(User user) {
        return JWT.create().withExpiresAt(DateUtil.offsetDay(new Date(), 1)).withAudience(user.getId().toString())
                .sign(Algorithm.HMAC256(user.getPassword()));
    }

    /**
     * 获取token中的用户信息
     * @return
     */
    public static User getUser() {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            String token = request.getHeader("token");
            String aud = JWT.decode(token).getAudience().get(0);
            Integer userId = Integer.valueOf(aud);
            return staticUserMapper.selectById(userId);
        } catch (Exception e) {
            log.error("解析token失败", e);
            return null;
        }
    }
    
}

12.2 基础控制类 BaseController

因为有些操作,我们所有的controller都会使用,因此我创建了BaseController用于存放这些会反复用到的、重要的函数。如:

l 通过token获取user

l 通过cookie获取token,进而获取token

l 通过cookie判断用户登陆状态和信息

其他controller只要继承这玩意就行。

*后端的几乎所有敏感操作都会通过cookie进行身份的验证!*

@RestController
public class BaseController {

    @Resource
    UserMapper userMapper;

    @Autowired
    protected HttpServletRequest request;

    /**
     * 根据token获取用户信息
     * @return user
     */
    public User getUserFromToken() {
        String token = request.getHeader("token");
        String aud = JWT.decode(token).getAudience().get(0);
        Integer userId = Integer.valueOf(aud);
        return userMapper.selectById(userId);
    }
    /**
     * 根据cookie获得token,获得用户信息
     * @return user
     */
    public User getUserFromCookie(){
        String token = null;
        for(int i = 0; i < request.getCookies().length; i++){
            if(request.getCookies()[i].getName().equals("token")){
                token = request.getCookies()[i].getValue();
            }
        }
        if(token!=null){
            String aud = JWT.decode(token).getAudience().get(0);
            Integer userId = Integer.valueOf(aud);
            if(userMapper.selectById(userId)!=null){
                User retUser = userMapper.selectById(userId);
                retUser.setPassword("");
                return retUser;
            }else {
                return null;
            }
        }else {
            return null;
        }



    }
    /**
     * 判断登录状态及用户是否存在
     * @return boolean
     */
    //验证登陆状态
    public boolean verifyLoginStatus(){
        if (request.getCookies() == null) {
            return false;
        }
        String token = null;
        for (int i = 0; i < request.getCookies().length; i++) {
            if (request.getCookies()[i].getName().equals("token")) {
                token = request.getCookies()[i].getValue();
            }
        }
        if (token == null) return false;
        return getUserFromCookie() != null;
    }
    /*验证登录模板:
        if(!verifyLoginStatus()){
            return Result.error("-1","登录状态有误,请重新登陆!");
        }
     */
}

12.3 组装cookie及注销

//登录 post /login
    @PostMapping("/login")
    public Result<?> login(@RequestBody User userParam, HttpServletResponse response) {
        User userPwd = userMapper.selectPwdByName(userParam.getUsername());
        if(userPwd==null){
            return Result.error("-1","用户名错误");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", userParam.getUsername());
        queryWrapper.eq("password", userPwd.getPassword());
        User res = userMapper.selectOne(queryWrapper);

        // 判断密码是否正确
        if (!bCryptPasswordEncoder.matches(userParam.getPassword(), userPwd.getPassword())) {
            return Result.error("-1", "密码错误");
        }
        if (res == null) {
            return Result.error("-1", "用户名或密码错误");
        }

        // 生成token
        String token = TokenUtils.genToken(res);
        //res.setToken(token);

        //组装cookie
        final ResponseCookie responseCookie = ResponseCookie
                .from("token", token)
                //.secure(true)
                .httpOnly(true)
                .path("/")
                .maxAge(60 * 60 * 24 * 7)   //7天有效期
                .sameSite("Lax")
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());

        res.setPassword("");

        return Result.success(res);
    }

//注销
    @PostMapping("/logout")
    public Result<?> logOut(HttpServletResponse response){
        if(request.getCookies()==null){
            return Result.error("-1","未登录");
        }
        final ResponseCookie responseCookie = ResponseCookie
                .from("token", "")
                //.secure(true)
                .httpOnly(true)
                .path("/")
                .maxAge(0)
                .sameSite("Lax")
                .build();
        response.addHeader(HttpHeaders.SET_COOKIE, responseCookie.toString());

        return Result.success();
    }

13 文件上传

13.1 普通文件上传

@RestController
@RequestMapping("/file")
public class FileController extends BaseController {
    @Value("${server.port}")
    private String port;

    @Value("${file.ip}")
    private String ip;


    /**
     * 上传接口
     *
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/upload")
    public Result<?> upload(MultipartFile file) throws IOException {
        if (file == null) {
            return Result.error("-1", "文件为空");
        }

        String fileType = file.getContentType();
        if(fileType==null){
            return Result.error("-1", "未知文件格式");
        }
        if (!fileType.contains("image/")) {
            return Result.error("-1", "文件格式上传错误");
        }

        if(!verifyLoginStatus()){
            return Result.error("-1","登录状态有误,请重新登陆!");
        }


        String originalFilename = file.getOriginalFilename();  // 获取源文件的名称
        // 定义文件的唯一标识(前缀)
        String fileUUID = IdUtil.fastSimpleUUID();
        String rootFilePath = System.getProperty("user.dir") + "/files/" + fileUUID + "_" + originalFilename;  // 获取上传的路径
        File rootFile = new File(rootFilePath);
        if (!rootFile.getParentFile().exists()) {
            rootFile.getParentFile().mkdirs();
        }
        FileUtil.writeBytes(file.getBytes(), rootFilePath);  // 把文件写入到上传的路径
        return Result.success("/file/" + fileUUID);  // 返回结果 url
    }


    /**
     * 下载接口
     *
     * @param fileUUID
     * @param response
     */
    @GetMapping("/{fileUUID}")
    public Result<?> getFiles(@PathVariable String fileUUID, HttpServletResponse response) {
        OutputStream os;  // 新建一个输出流对象
        String basePath = System.getProperty("user.dir") + "/files/";  // 定于文件上传的根路径
        List<String> fileNames = FileUtil.listFileNames(basePath);  // 获取所有的文件名称
        String fileName = fileNames.stream().filter(name -> name.contains(fileUUID)).findAny().orElse("");  // 找到跟参数一致的文件
        try {
            if (StrUtil.isNotEmpty(fileName)) {
                response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
                response.setContentType("application/octet-stream");


                response.setHeader("cache-control","max-age=5184000");
                byte[] bytes = FileUtil.readBytes(basePath + fileName);  // 通过文件的路径读取文件字节流
                os = response.getOutputStream();   // 通过输出流返回文件
                os.write(bytes);
                os.flush();
                os.close();
            }
        } catch (Exception e) {
            return Result.error("-1","文件下载失败");
        }
        return Result.success();
    }


}

13.2 头像上传

此类上传需记录url到数据库

 @PostMapping("/upload_avatar")
    public Result<?> upload(MultipartFile file) throws IOException {
        if (file == null) {
            return Result.error("-1", "文件为空");
        }

        String fileType = file.getContentType();
        if (fileType == null) {
            return Result.error("-1", "未知文件格式");
        }
        if (!fileType.contains("image/")) {
            return Result.error("-1", "文件格式上传错误");
        }

        if (!verifyLoginStatus()) {
            return Result.error("-1", "登录状态有误,请重新登陆!");
        }


        String originalFilename = file.getOriginalFilename();  // 获取源文件的名称
        // 定义文件的唯一标识(前缀)
        String fileUUID = IdUtil.fastSimpleUUID();
        String rootFilePath = System.getProperty("user.dir") + "/files/" + fileUUID + "_" + originalFilename;  // 获取上传的路径
        File rootFile = new File(rootFilePath);
        if (!rootFile.getParentFile().exists()) {
            rootFile.getParentFile().mkdirs();
        }
        FileUtil.writeBytes(file.getBytes(), rootFilePath);  // 把文件写入到上传的路径

        User user = getUserFromCookie();
        Profile profile = profileMapper.selectById(user.getId());
        profile.setAvatar("/file/" + fileUUID);
        profileMapper.updateById(profile);

        return Result.success("/file/" + fileUUID);  // 返回结果 url


    }

标签:return,springboot,private,token,自用,Result,轮子,public,String
From: https://www.cnblogs.com/fordsonliuzhiwen/p/17001821.html

相关文章