Spring Boot 项目搭建模板

Spring Boot 项目搭建模板

首先来看一下项目结构

Spring Boot 项目搭建模板_第1张图片

看看项目实际都集成了哪些模块

  • 全局异常处理
  • 响应码枚举
  • 自定义统一响应体
  • 全局处理响应数据
  • 拦截器
  • 全局跨域
  • 阿里短信服务
  • 阿里 OSS 服务
  • MyBatis
  • MySQL
  • Redis
  • Swagger
  • JWT
  • Lombok
  • Https 认证

全局异常处理

首先来看一下代码的目录结构

Spring Boot 项目搭建模板_第2张图片

  1. 自定义异常

    /**
     * @description: 全局异常处理类
     * @author: wubowen
     * @date: 2021/5/25 0025 10:04
     */
    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 处理自定义异常
         */
        @ExceptionHandler(value = BadRequestException.class)
        public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
            return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
        }
    
        /**
         * 统一返回
         */
        private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
            return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
        }
    }
    
  2. 全局异常处理类

    /**
     * @description: 全局异常处理类
     * @author: wubowen
     * @date: 2021/5/25 0025 10:04
     */
    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 处理自定义异常
         */
        @ExceptionHandler(value = BadRequestException.class)
        public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
            return buildResponseEntity(ApiError.error(e.getStatus(), e.getMessage()));
        }
    
        /**
         * 统一返回
         */
        private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
            return new ResponseEntity<>(apiError, HttpStatus.valueOf(apiError.getStatus()));
        }
    }
    
  3. 统一返回体实现

    /**
     * @description: 全局异常处理类
     * @author: wubowen
     * @date: 2021/5/25 0025 10:04
     */
    @Slf4j
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    
        /**
         * 处理自定义异常
         */
        @ExceptionHandler(value = BadRequestException.class)
        public ResultV0<String> badRequestException(BadRequestException e) {
            return new ResultV0<>(ResultCode.VALIDATE_FAILED, null);
        }
    }
    

响应码枚举

/**
 * @description: 响应码枚举类
 * @author: wubowen
 * @date: 2021/5/25 0025 14:46
 */
@Getter
public enum ResultCode {
    SUCCESS(1000, "操作成功"),
    VALIDATE_FAILED(1001, "参数校验失败");

    private int code;
    private String msg;

    ResultCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}

自定义统一响应体

package com.stork.base;

import com.stork.utils.enums.ResultCode;
import lombok.Getter;

/**
 * @description: 自定义统一的响应体
 * @author: wubowen
 * @date: 2021/5/26 0026 16:48
 */
@Getter
public class ResultV0<T> {
    /**
     * 状态码
     */
    private int code;
    /**
     * 响应信息
     */
    private String msg;
    /**
     * 响应的具体数据
     */
    private T data;

    public ResultV0(T data) {
        this(ResultCode.SUCCESS, data);
    }

    public ResultV0(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = data;
    }

    public ResultV0(ResultCode resultCode) {
        this.code = resultCode.getCode();
        this.msg = resultCode.getMsg();
        this.data = null;
    }

    public ResultV0(ResultCode resultCode, String msg) {
        this.code = resultCode.getCode();
        this.msg = msg;
        this.data = null;
    }
}

全局处理响应数据

package com.stork.global;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stork.base.ResultV0;
import com.stork.exception.BadRequestException;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @description: 全局处理响应数据
 * 包装返回的实体类,统一返回体
 * @author: wubowen
 * @date: 2021/5/26 0026 17:03
 */
@RestControllerAdvice(basePackages = "com.stork.handler")
public class ResponseControllerAdvice implements ResponseBodyAdvice<Object> {
    public static final String NEWSIMAGEUPLOADPATH = "/news/uploadImageForNews";
    public static final String NEWSVIDEOUPLOADPATH = "/news/uploadVideoForNews";

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
        /**
         * 如果接口返回的类型本身就是ResultV0,那就没有必要进行额外操作,直接返回false
         */

        return !methodParameter.getParameterType().equals(ResultV0.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (serverHttpRequest.getURI().getPath().equals(NEWSIMAGEUPLOADPATH)
            ||serverHttpRequest.getURI().getPath().equals(NEWSVIDEOUPLOADPATH)) {
            return o;
        }

        /**
         * String类型不能直接包装,所以要进行些特别的处理
         */
        if (methodParameter.getGenericParameterType().equals(String.class)) {
            ObjectMapper objectMapper = new ObjectMapper();
            try {
                /**
                 * String类型不能直接包装,所以要进行些特别的处理
                 */
                return objectMapper.writeValueAsString(new ResultV0<>(o));
            } catch (JsonProcessingException e) {
                throw new BadRequestException("返回String类型错误");
            }
        }

        /**
         * 将原本的数据包装到ResultVO里面
         */
        return new ResultV0<>(o);
    }
}

@RestControllerAdvice注解必须加上扫描包的路径,否则会影响Swagger的使用

拦截器

  1. 新建一个拦截器

    package com.stork.global;
    
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @description: 测试拦截器
     * @author: wubowen
     * @date: 2021/7/12 0012 15:31
     */
    public class TestIntercepter implements HandlerInterceptor {
    
        /** 在请求处理之前被调用,在Controller方法调用之前调用 **/
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 此处为某些接口放行,不走以下逻辑
            if (request.getRequestURI().contains("/.")) {
                return true;
            }
            return false;
        }
    
        /** 在请求处理之后被调用,但是在视图渲染之前调用 **/
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
    }
    
  2. 配置新建的拦截器

    package com.stork.config;
    
    import com.stork.global.TestIntercepter;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    /**
     * @description: 配置拦截器
     * @author: wubowen
     * @date: 2021/7/12 0012 15:47
     */
    @Configuration
    public class TestIntercepterConfiguration implements WebMvcConfigurer {
    
        @Bean
        public HandlerInterceptor getTestInterceptor() {
            return new TestIntercepter();
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(getTestInterceptor()).addPathPatterns("/test");
        }
    }
    

全局跨域配置

看下目录结构

Spring Boot 项目搭建模板_第3张图片

/**
 * @description: 跨域配置
 * @author: wubowen
 * @date: 2021/5/26 0026 10:17
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    /**
     * 跨域配置
     * addMapping:对哪种格式的请求路径进行处理
     * allowedHeaders:允许的请求头
     * allowedMethods:允许的请求方法
     * maxAge:探测请求的有效期
     * allowedOrigins:支持的域
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(1800)
                .allowedOrigins("*");
    }
}

阿里短信服务

  1. 配置yml文件

    # 阿里短信服务配置
    sms:
      sign-name : **********
      template-code : **********
      access-keyID : **********
      access-keySecret : **********
    
  2. 导入短信依赖

    
    <dependency>
        <groupId>com.aliyungroupId>
        <artifactId>aliyun-java-sdk-coreartifactId>
        <version>4.5.3version>
    dependency>
    

Spring Boot 项目搭建模板_第4张图片

  1. 创建短信接口

    package com.stork.service;
    
    /**
     * @description: 短信服务
     * @author: wubowen
     * @date: 2021/7/12 0012 17:51
     */
    public interface SmsService {
    
        /**
         * 根据电话号码发送短信
         * @param phone 电话号码
         */
        void sendSmsCode(String phone);
    
        /**
         * 验证短信验证码正确性
         * @param phone 电话号码
         * @param code 验证码
         * @return
         */
        boolean validSmsCode(String phone, String code);
    
    }
    
  2. 完成短信实际业务

    @Service
    public class SmsServiceImpl implements SmsService {
        @Value("${sms.access-keyID}")
        private String accessID;
        @Value("${sms.access-keySecret}")
        private String accessKey;
        @Value("${sms.sign-name}")
        private String signName;
        @Value("${sms.template-code}")
        private String templageCode;
    
        @Autowired
        private RedisUtils redisUtils;
        // JSON处理工具
        @Autowired
        private ObjectMapper objectMapper;
    
        @Override
        public void sendSmsCode(String phone) {
            // 是否频繁发送验证码
            if (isSendOften(phone)) {
                throw new BadRequestException("验证码请求过于频繁");
            }
            Sms sms = makeCode(phone);
            ObjectNode smsJson = objectMapper.createObjectNode();
            smsJson.put("code", sms.getSmsCode());
            smsJson.put("product", "Stork");
            try {
                CommonResponse commonResponse = send(phone, signName, templageCode, smsJson);
                if (commonResponse.getData() != null) {
                    redisCode(sms);
                }
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public boolean validSmsCode(String phone, String code) {
            try {
                Sms sms = objectMapper.readValue(String.valueOf(redisUtils.get(phone)), Sms.class);
                if (sms == null) {
                    return false;
                }
    
                if (sms.getSmsCode().equals(code)) {
                    return true;
                }
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
    
            return false;
        }
    
        /**
         * 生成四位随机的验证码
         * @param phone 电话号码
         * @return
         */
        private Sms makeCode(String phone) {
            String smsCode = RandomUtils.getRandomString(4);
            Sms sms = new Sms();
            sms.setPhone(phone);
            sms.setSmsCode(smsCode);
            sms.setTime(System.currentTimeMillis());
            return sms;
        }
    
        /**
         * 验证这个电话短信发送是否频繁
         * @param phone 电话号码
         * @return
         */
        private boolean isSendOften(String phone) {
            if (redisUtils.get(phone) == null) {
                return false;
            }
    
            // 反序列化
            Sms sms = null;
            try {
                sms = objectMapper.readValue(String.valueOf(redisUtils.get(phone)), Sms.class);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            if (sms.getTime() + 60*1000 >= System.currentTimeMillis()) {
                return true;
            }
    
            return false;
        }
    
        /**
         * 将验证码缓存到redis,并且制定缓存时间为10分钟
         * @param sms 短信实体
         */
        private void redisCode(Sms sms) throws JsonProcessingException {
            redisUtils.set(sms.getPhone(), objectMapper.writeValueAsString(sms), 10L, TimeUnit.MINUTES);
        }
    
        /**
         * 发送短信
         * @param phone
         * @param signName
         * @param templageCode
         * @param params
         * @return
         */
        private CommonResponse send(String phone, String signName, String templageCode, ObjectNode params) throws JsonProcessingException {
            DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessID, accessKey);
            IAcsClient client = new DefaultAcsClient(profile);
    
            CommonResponse response = null;
            CommonRequest request = new CommonRequest();
            request.setSysMethod(MethodType.POST);
            request.setSysDomain("dysmsapi.aliyuncs.com");
            request.setSysVersion("2017-05-25");
            request.setSysAction("SendSms");
            request.putQueryParameter("RegionId", "cn-chengdu");
            request.putQueryParameter("PhoneNumbers", phone);
            request.putQueryParameter("SignName", signName);
            request.putQueryParameter("TemplateCode", templageCode);
            request.putQueryParameter("TemplateParam", objectMapper.writeValueAsString(params));
            try {
                response = client.getCommonResponse(request);
            } catch (ServerException e) {
                e.printStackTrace();
            } catch (ClientException e) {
                e.printStackTrace();
            }
            return response;
        }
    
    }
    

阿里 OSS 服务

  1. 阿里云基础配置

    # 阿里云基础配置
    aliyun:
      accessKeyId : **********
      accessKeySecret : **********
      chengduEndpoint : https://oss-cn-chengdu.aliyuncs.com
    
  2. 导入 OSS 依赖

    
    <dependency>
        <groupId>com.aliyun.ossgroupId>
        <artifactId>aliyun-sdk-ossartifactId>
        <version>3.10.2version>
    dependency>
    
  3. 创建 OSS 接口

    package com.stork.service;
    
    import com.aliyun.oss.model.OSSObject;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * @description: OSS服务
     * @author: wubowen
     * @date: 2021/7/14 0014 15:18
     */
    public interface OSSService {
    
        /**
         *  文件上传接口
         * @param endpoint
         * @param bucketName
         * @param objectName 上传文件到OSS时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
         * @param file 上传文件
         */
        void uploadFile(String endpoint, String bucketName, String objectName, MultipartFile file);
    
        /**
         *  文件下载接口
         * @param endpoint
         * @param bucketName
         * @param objectName 从OSS下载文件时需要指定包含文件后缀在内的完整路径,例如abc/efg/123.jpg。
         * @return
         */
        OSSObject downloadFile(String endpoint, String bucketName, String objectName);
    
        /**
         * 文件删除接口
         * @param endpoint
         * @param bucketName
         * @param objectName 填写文件完整路径。文件完整路径中不能包含Bucket名称。
         */
        void deleteFile(String endpoint, String bucketName, String objectName);
    
    }
    
  4. 完成 OSS 实现类

    package com.stork.service.Impl;
    
    import com.aliyun.oss.OSS;
    import com.aliyun.oss.OSSClientBuilder;
    import com.aliyun.oss.model.OSSObject;
    import com.stork.service.OSSService;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    
    /**
     * @description: some desc
     * @author: wubowen
     * @date: 2021/7/14 0014 15:45
     */
    @Service
    public class OSSServiceImpl implements OSSService {
        @Value("${aliyun.accessKeyId}")
        private String accessKeyId;
        @Value("${aliyun.accessKeySecret}")
        private String accessKeySecret;
    
        @Override
        public void uploadFile(String endpoint, String bucketName, String objectName, MultipartFile file) {
    
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
    
            // 上传文件到指定的存储空间(bucketName)并将其保存为指定的文件名称(objectName)。
            try {
                ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(file.getBytes()));
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            // 关闭OSSClient。
            ossClient.shutdown();
        }
    
        @Override
        public OSSObject downloadFile(String endpoint, String bucketName, String objectName) {
            return null;
        }
    
        @Override
        public void deleteFile(String endpoint, String bucketName, String objectName) {
            // 创建OSSClient实例。
            OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
            // 删除文件或目录。如果要删除目录,目录必须为空。
            ossClient.deleteObject(bucketName, objectName);
            // 关闭OSSClient。
            ossClient.shutdown();
        }
    }
    

MyBatis

  1. 引入依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>1.3.2</version>
    </dependency>
    
  2. 扫描指定包下所有Mapper
    Spring Boot 项目搭建模板_第5张图片
    注意:如果mapper配置文件没有放在resources文件夹下需要重新在pom.xml中指定资源文件夹路径

<build>
    
    <resources>
        <resource>
            <directory>src/main/javadirectory>
            <includes>
                <include>**/*.xmlinclude>
            includes>
        resource>
        <resource>
            <directory>src/main/resourcesdirectory>
        resource>
    resources>
build>

MySQL

  1. 引入依赖

    
    <dependency>
        <groupId>mysqlgroupId>
        <artifactId>mysql-connector-javaartifactId>
        <scope>runtimescope>
    dependency>
    
  2. 配置 yml 文件

    spring:
      datasource:
        url: jdbc:mysql://主机名:3306/yunying?useUnicode=true&serverTimezone=GMT%2B8&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
        username: 账号
        password: 密码
        driver-class-name: com.mysql.cj.jdbc.Driver
    

Redis

  1. 引入 Redis 依赖

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
  2. 配置 Redis

    redis:
      #主机IP地址
      host : localhost
      #Redis服务器连接端口
      port : 6379
      #Redis服务器连接密码(默认为空)
      #password :
      #连接池最大连接数(使用负值表示没有限制)
      max-active : 8
      #连接池最大阻塞等待时间(使用负值表示没有限制)
      max-wait : -1
      #连接池中的最大空闲连接
      max-idle : 8
      #连接池中的最小空闲连接
      min-idle : 0
      #连接超时时间(毫秒)
      timeout : 30000
    
  3. 编写 Redis 工具类

    /**
     * @description: Redis工具类
     * @author: wubowen
     * @date: 2021/5/27 0027 14:39
     */
    @Component
    public class RedisUtils {
        private static final Logger log = LoggerFactory.getLogger(RedisUtils.class);
        private RedisTemplate<Object, Object> redisTemplate;
    //    @Value("${jwt.online-key}")
    //    private String onlineKey;
    
        public RedisUtils(RedisTemplate<Object, Object> redisTemplate) {
            this.redisTemplate = redisTemplate;
        }
    
        /**
         * 指定缓存失效时间
         *
         * @param key  键
         * @param time 时间(秒)
         */
        public boolean expire(String key, long time) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, TimeUnit.SECONDS);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
            return true;
        }
    
        /**
         * 指定缓存失效时间
         *
         * @param key      键
         * @param time     时间(秒)
         * @param timeUnit 单位
         */
        public boolean expire(String key, long time, TimeUnit timeUnit) {
            try {
                if (time > 0) {
                    redisTemplate.expire(key, time, timeUnit);
                }
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
            return true;
        }
    
        /**
         * 根据 key 获取过期时间
         *
         * @param key 键 不能为null
         * @return 时间(秒) 返回0代表为永久有效
         */
        public long getExpire(Object key) {
            return redisTemplate.getExpire(key, TimeUnit.SECONDS);
        }
    
        /**
         * 查找匹配key
         *
         * @param pattern key
         * @return /
         */
        public List<String> scan(String pattern) {
            ScanOptions options = ScanOptions.scanOptions().match(pattern).build();
            RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
            RedisConnection rc = Objects.requireNonNull(factory).getConnection();
            Cursor<byte[]> cursor = rc.scan(options);
            List<String> result = new ArrayList<>();
            while (cursor.hasNext()) {
                result.add(new String(cursor.next()));
            }
            try {
                RedisConnectionUtils.releaseConnection(rc, factory);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            return result;
        }
    
        /**
         * 分页查询 key
         *
         * @param patternKey key
         * @param page       页码
         * @param size       每页数目
         * @return /
         */
        public List<String> findKeysForPage(String patternKey, int page, int size) {
            ScanOptions options = ScanOptions.scanOptions().match(patternKey).build();
            RedisConnectionFactory factory = redisTemplate.getConnectionFactory();
            RedisConnection rc = Objects.requireNonNull(factory).getConnection();
            Cursor<byte[]> cursor = rc.scan(options);
            List<String> result = new ArrayList<>(size);
            int tmpIndex = 0;
            int fromIndex = page * size;
            int toIndex = page * size + size;
            while (cursor.hasNext()) {
                if (tmpIndex >= fromIndex && tmpIndex < toIndex) {
                    result.add(new String(cursor.next()));
                    tmpIndex++;
                    continue;
                }
                // 获取到满足条件的数据后,就可以退出了
                if (tmpIndex >= toIndex) {
                    break;
                }
                tmpIndex++;
                cursor.next();
            }
            try {
                RedisConnectionUtils.releaseConnection(rc, factory);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }
            return result;
        }
    
        /**
         * 判断key是否存在
         *
         * @param key 键
         * @return true 存在 false不存在
         */
        public boolean hasKey(String key) {
            try {
                return redisTemplate.hasKey(key);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 删除缓存
         *
         * @param key 可以传一个值 或多个
         */
        public void del(String... keys) {
            if (keys != null && keys.length > 0) {
                if (keys.length == 1) {
                    boolean result = redisTemplate.delete(keys[0]);
                    log.debug("--------------------------------------------");
                    log.debug(new StringBuilder("删除缓存:").append(keys[0]).append(",结果:").append(result).toString());
                    log.debug("--------------------------------------------");
                } else {
                    Set<Object> keySet = new HashSet<>();
                    for (String key : keys) {
                        keySet.addAll(redisTemplate.keys(key));
                    }
                    long count = redisTemplate.delete(keySet);
                    log.debug("--------------------------------------------");
                    log.debug("成功删除缓存:" + keySet.toString());
                    log.debug("缓存删除数量:" + count + "个");
                    log.debug("--------------------------------------------");
                }
            }
        }
    
        // ============================String=============================
    
        /**
         * 普通缓存获取
         *
         * @param key 键
         * @return 值
         */
        public Object get(String key) {
            return key == null ? null : redisTemplate.opsForValue().get(key);
        }
    
        /**
         * 批量获取
         *
         * @param keys
         * @return
         */
        public List<Object> multiGet(List<String> keys) {
            Object obj = redisTemplate.opsForValue().multiGet(Collections.singleton(keys));
            return null;
        }
    
        /**
         * 普通缓存放入
         *
         * @param key   键
         * @param value 值
         * @return true成功 false失败
         */
        public boolean set(String key, Object value) {
            try {
                redisTemplate.opsForValue().set(key, value);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 普通缓存放入并设置时间
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒) time要大于0 如果time小于等于0 将设置无限期
         * @return true成功 false 失败
         */
        public boolean set(String key, Object value, long time) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
                } else {
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 普通缓存放入并设置时间
         *
         * @param key      键
         * @param value    值
         * @param time     时间
         * @param timeUnit 类型
         * @return true成功 false 失败
         */
        public boolean set(String key, Object value, long time, TimeUnit timeUnit) {
            try {
                if (time > 0) {
                    redisTemplate.opsForValue().set(key, value, time, timeUnit);
                } else {
                    set(key, value);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        // ================================Map=================================
    
        /**
         * HashGet
         *
         * @param key  键 不能为null
         * @param item 项 不能为null
         * @return 值
         */
        public Object hget(String key, String item) {
            return redisTemplate.opsForHash().get(key, item);
        }
    
        /**
         * 获取hashKey对应的所有键值
         *
         * @param key 键
         * @return 对应的多个键值
         */
        public Map<Object, Object> hmget(String key) {
            return redisTemplate.opsForHash().entries(key);
    
        }
    
        /**
         * HashSet
         *
         * @param key 键
         * @param map 对应多个键值
         * @return true 成功 false 失败
         */
        public boolean hmset(String key, Map<String, Object> map) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * HashSet 并设置时间
         *
         * @param key  键
         * @param map  对应多个键值
         * @param time 时间(秒)
         * @return true成功 false失败
         */
        public boolean hmset(String key, Map<String, Object> map, long time) {
            try {
                redisTemplate.opsForHash().putAll(key, map);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         *
         * @param key   键
         * @param item  项
         * @param value 值
         * @return true 成功 false失败
         */
        public boolean hset(String key, String item, Object value) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 向一张hash表中放入数据,如果不存在将创建
         *
         * @param key   键
         * @param item  项
         * @param value 值
         * @param time  时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
         * @return true 成功 false失败
         */
        public boolean hset(String key, String item, Object value, long time) {
            try {
                redisTemplate.opsForHash().put(key, item, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 删除hash表中的值
         *
         * @param key  键 不能为null
         * @param item 项 可以使多个 不能为null
         */
        public void hdel(String key, Object... item) {
            redisTemplate.opsForHash().delete(key, item);
        }
    
        /**
         * 判断hash表中是否有该项的值
         *
         * @param key  键 不能为null
         * @param item 项 不能为null
         * @return true 存在 false不存在
         */
        public boolean hHasKey(String key, String item) {
            return redisTemplate.opsForHash().hasKey(key, item);
        }
    
        /**
         * hash递增 如果不存在,就会创建一个 并把新增后的值返回
         *
         * @param key  键
         * @param item 项
         * @param by   要增加几(大于0)
         * @return
         */
        public double hincr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, by);
        }
    
        /**
         * hash递减
         *
         * @param key  键
         * @param item 项
         * @param by   要减少记(小于0)
         * @return
         */
        public double hdecr(String key, String item, double by) {
            return redisTemplate.opsForHash().increment(key, item, -by);
        }
    
        // ============================set=============================
    
        /**
         * 根据key获取Set中的所有值
         *
         * @param key 键
         * @return
         */
        public Set<Object> sGet(String key) {
            try {
                return redisTemplate.opsForSet().members(key);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return null;
            }
        }
    
        /**
         * 根据value从一个set中查询,是否存在
         *
         * @param key   键
         * @param value 值
         * @return true 存在 false不存在
         */
        public boolean sHasKey(String key, Object value) {
            try {
                return redisTemplate.opsForSet().isMember(key, value);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 将数据放入set缓存
         *
         * @param key    键
         * @param values 值 可以是多个
         * @return 成功个数
         */
        public long sSet(String key, Object... values) {
            try {
                return redisTemplate.opsForSet().add(key, values);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        /**
         * 将set数据放入缓存
         *
         * @param key    键
         * @param time   时间(秒)
         * @param values 值 可以是多个
         * @return 成功个数
         */
        public long sSetAndTime(String key, long time, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().add(key, values);
                if (time > 0) {
                    expire(key, time);
                }
                return count;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        /**
         * 获取set缓存的长度
         *
         * @param key 键
         * @return
         */
        public long sGetSetSize(String key) {
            try {
                return redisTemplate.opsForSet().size(key);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        /**
         * 移除值为value的
         *
         * @param key    键
         * @param values 值 可以是多个
         * @return 移除的个数
         */
        public long setRemove(String key, Object... values) {
            try {
                Long count = redisTemplate.opsForSet().remove(key, values);
                return count;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        // ===============================list=================================
    
        /**
         * 获取list缓存的内容
         *
         * @param key   键
         * @param start 开始
         * @param end   结束 0 到 -1代表所有值
         * @return
         */
        public List<Object> lGet(String key, long start, long end) {
            try {
                return redisTemplate.opsForList().range(key, start, end);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return null;
            }
        }
    
        /**
         * 获取list缓存的长度
         *
         * @param key 键
         * @return
         */
        public long lGetListSize(String key) {
            try {
                return redisTemplate.opsForList().size(key);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        /**
         * 通过索引 获取list中的值
         *
         * @param key   键
         * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
         * @return
         */
        public Object lGetIndex(String key, long index) {
            try {
                return redisTemplate.opsForList().index(key, index);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return null;
            }
        }
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @return
         */
        public boolean lSet(String key, Object value) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒)
         * @return
         */
        public boolean lSet(String key, Object value, long time) {
            try {
                redisTemplate.opsForList().rightPush(key, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @return
         */
        public boolean lSet(String key, List<Object> value) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 将list放入缓存
         *
         * @param key   键
         * @param value 值
         * @param time  时间(秒)
         * @return
         */
        public boolean lSet(String key, List<Object> value, long time) {
            try {
                redisTemplate.opsForList().rightPushAll(key, value);
                if (time > 0) {
                    expire(key, time);
                }
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 根据索引修改list中的某条数据
         *
         * @param key   键
         * @param index 索引
         * @param value 值
         * @return /
         */
        public boolean lUpdateIndex(String key, long index, Object value) {
            try {
                redisTemplate.opsForList().set(key, index, value);
                return true;
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return false;
            }
        }
    
        /**
         * 移除N个值为value
         *
         * @param key   键
         * @param count 移除多少个
         * @param value 值
         * @return 移除的个数
         */
        public long lRemove(String key, long count, Object value) {
            try {
                return redisTemplate.opsForList().remove(key, count, value);
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                return 0;
            }
        }
    
        /**
         * @param prefix 前缀
         * @param ids    id
         */
        public void delByKeys(String prefix, Set<Long> ids) {
            Set<Object> keys = new HashSet<>();
            for (Long id : ids) {
                keys.addAll(redisTemplate.keys(new StringBuffer(prefix).append(id).toString()));
            }
            long count = redisTemplate.delete(keys);
            // 此处提示可自行删除
            log.debug("--------------------------------------------");
            log.debug("成功删除缓存:" + keys.toString());
            log.debug("缓存删除数量:" + count + "个");
            log.debug("--------------------------------------------");
        }
    }
    
  4. 测试

    @ApiOperation("创建临时用户(用于测试Redis)")
    @PostMapping("/addTempUser")
    public String addTempUser(@RequestBody User user) {
        redisUtils.set(user.getUsername(), user.getPassword());
        String password = (String) redisUtils.get(user.getUsername());
        return "您设置的密码为:" + password;
    }
    

Swagger

  1. 依赖导入

    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger2artifactId>
        <version>2.9.2version>
    dependency>
    
    <dependency>
        <groupId>io.springfoxgroupId>
        <artifactId>springfox-swagger-uiartifactId>
        <version>2.9.2version>
    dependency>
    
  2. 配置

    package com.stork.config;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import springfox.documentation.builders.ParameterBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.schema.ModelRef;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spring.web.plugins.Docket;
    import springfox.documentation.swagger2.annotations.EnableSwagger2;
    import springfox.documentation.service.Parameter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @description: 接口管理工具Swagger的配置类
     * @author: wubowen
     * @date: 2021/5/25 0025 17:13
     */
    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        public Docket api(){
            // 如果接口需要token验证,那么在header中新增一个参数
                  Parameter tokenPara = new ParameterBuilder()
                    .name("token")
                    .modelRef(new ModelRef("string"))
                    .parameterType("header")
                    .required(false)
                    .build();
            List<Parameter> paras = new ArrayList<>();
            paras.add(tokenPara);
    
            return new Docket(DocumentationType.SWAGGER_2)
                    .select()
                    .apis(RequestHandlerSelectors.any())
                    .paths(PathSelectors.any())
                    .build()
                    .globalOperationParameters(paras);
        }
    }
    

JWT

  1. 引入 JWT 依赖

    
    <dependency>
        <groupId>com.auth0groupId>
        <artifactId>java-jwtartifactId>
        <version>3.8.2version>
    dependency>
    
  2. 新建 JWT 工具类

    package com.stork.utils;
    
    import com.auth0.jwt.JWT;
    import com.auth0.jwt.JWTVerifier;
    import com.auth0.jwt.algorithms.Algorithm;
    import com.auth0.jwt.interfaces.Claim;
    import com.auth0.jwt.interfaces.DecodedJWT;
    import com.stork.entities.StorkUser;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @description: JWT工具类,生成JWT和认证
     * @author: wubowen
     * @date: 2021/11/13 0013 9:48
     */
    public class JwtUtil {
        private static final Logger logger = LoggerFactory.getLogger(JwtUtil.class);
        /**
         * 秘钥
         */
        private static final String SECRET = "yunying_secret";
        /**
         * 过期时间
         */
        private static final long EXPIRATION = 2592000L;
    
        /**
         * 生成用户token,设置超时时间
         * @param storkUser
         * @return
         */
        public static String createToken(StorkUser storkUser) {
            // 过期时间
            Date expireDate = new Date(System.currentTimeMillis() + EXPIRATION * 1000);
            // 设置JWT规则
            Map<String, Object> rule = new HashMap<>();
            rule.put("alg", "HS256");
            rule.put("typ", "JWT");
            // 创建token
            String token = JWT.create()
                    .withHeader(rule)
                    .withClaim("username", storkUser.getUsername())
                    .withExpiresAt(expireDate)
                    .withIssuedAt(new Date())
                    .sign(Algorithm.HMAC256(SECRET));
            return token;
        }
    
        /**
         * 校验token并解析token
         * @param token 需要验证的token
         * @return
         */
        public static Map<String, Claim> verifyToken(String token) {
            DecodedJWT jwt = null;
            try {
                JWTVerifier verifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
                jwt = verifier.verify(token);
            } catch (Exception e) {
                logger.error(e.getMessage());
                logger.error("token解码异常");
                return null;
            }
            return jwt.getClaims();
        }
    }
    
  3. 拦截器中加入 token 验证

    package com.stork.global;
    
    import com.auth0.jwt.interfaces.Claim;
    import com.stork.entities.StorkUser;
    import com.stork.exception.BadRequestException;
    import com.stork.utils.JwtUtil;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Date;
    import java.util.Map;
    
    /**
     * @description: 接口请求拦截器
     * @author: wubowen
     * @date: 2021/7/12 0012 15:31
     */
    public class RequestIntercepter implements HandlerInterceptor {
    
        /** 在请求处理之前被调用,在Controller方法调用之前调用 **/
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 此处为某些接口放行,不走以下逻辑
            //if (request.getRequestURI().contains("/.")) {
            //    return true;
            //}
            // 获取header里的token
            final String token = request.getHeader("token");
            // 验证token的有效性
            if (token == null) {
                throw new BadRequestException("请求失败,请重新登录");
            }
            // 解析token
            Map<String, Claim> userData = JwtUtil.verifyToken(token);
            // 检查token是否过期
            Date expireDate = userData.get("exp").asDate();
            boolean isExpire = expireDate.after(new Date());
            if (!isExpire) {
                throw new BadRequestException("请求过期,请重新登录");
            }
            // 更新token
            StorkUser storkUser = new StorkUser();
            storkUser.setUsername(userData.get("username").asString());
            String newToken = JwtUtil.createToken(storkUser);
            response.setHeader("token", newToken);
    
            return true;
        }
    
        /** 在请求处理之后被调用,但是在视图渲染之前调用 **/
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    
        }
    
    }
    

Lombok

  1. 安装插件
    Spring Boot 项目搭建模板_第6张图片

  2. 引入依赖

    
    <dependency>
        <groupId>org.projectlombokgroupId>
        <artifactId>lombokartifactId>
        <optional>trueoptional>
    dependency>
    

Https 认证

  1. 生成证书

    输入命令行

    keytool -genkey -alias yunyinghttps -keyalg RSA -keysize 2048 -keystore sang.p12 -validity 365
    

Spring Boot 项目搭建模板_第7张图片
2. 拷贝证书到项目根目录

Spring Boot 项目搭建模板_第8张图片

  1. 配置 yml 文件

    server:
      port: 8080
      # https配置
      ssl:
        key-store: sang.p12
        key-alias: yunyinghttps
        key-store-password: ******
    

你可能感兴趣的:(Java后端,spring,boot,java,spring)