Java – SpringBoot基础项目 – UnBlog 博客

常用项目工具类下载:

AppHttpCodeEnum

用于定义WEB服务中出现各种问题的枚举类,可以规范项目中的错误表达方式。

package cn.unsoft.utils.enums;

public enum AppHttpCodeEnum {
    // 成功
    SUCCESS(200,"操作成功"),
    // 登录
    NEED_LOGIN(401,"需要登录后操作"),
    NO_OPERATOR_AUTH(403,"无权限操作"),
    SYSTEM_ERROR(500,"出现错误"),
    USERNAME_EXIST(501,"用户名已存在"),
    PHONENUMBER_EXIST(502,"手机号已存在"),
    EMAIL_EXIST(503, "邮箱已存在"),
    REQUIRE_USERNAME(504, "必需填写用户名"),
    REGISTER_INFO_ERROR(507, "用户信息不完整"),
    REGISTER_EXIST_ERROR(507, "用户已被注册"),

    UPLOAD_TYPE_ERROR(506, "上传的文件格式有误"),
    DOWNLOAD_ERROR(506, "文件下载失败"),
    LOGIN_ERROR(505,"用户名或密码错误");
    int code;
    String msg;

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

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

 

 

BeanCopyUtils

用于把数据表中的实体类,转化为输出到前端显示的Vo实体类或前端传到服务器的DTO实体类,把该赋值的自动赋值。

支持单个Bean转换,也支持List集合中的Bean转换。

package cn.unsoft.utils;

import org.springframework.beans.BeanUtils;

import java.lang.reflect.Constructor;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 用于封装 Bean 复制的工具类
 */
public class BeanCopyUtils {

    private BeanCopyUtils() {
    }

    /**
     * 把一个数据库实体对象复制必要的数据到前端实体对象中
     *
     * @param bean
     * @param clazz
     * @param <Vo>
     * @return
     */
    public static <Vo> Vo copyBean(Object bean, Class<Vo> clazz) {

        Vo vo = null;
        try {
            Constructor<Vo> constructor = clazz.getConstructor();
            vo = constructor.newInstance();
            BeanUtils.copyProperties(bean, vo);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return vo;
    }

    /**
     * 把一个数据库实体对象集合复制必要的数据到前端实体对象中,并封装成集合
     * @param objList
     * @param clazz
     * @return
     * @param <T>
     * @param <Vo>
     */
    public static <T,Vo> List<Vo> copyBeanList(List<T> objList,Class<Vo> clazz){
        return objList.stream().map(item -> copyBean(item, clazz)).collect(Collectors.toList());
    }
}

 

 

使用方法:

BeanCopyUtils.copyBean(bean类,BeanVo.class)

 

 

JwtUtil

生成JWT密钥的工具,以token的形式发给前端保存,用以身份认证

package cn.unsoft.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * JWT工具类
 */
public class JwtUtil {

    //有效期为
    public static final Long JWT_TTL = 24*60 * 60 *1000L;// 60 * 60 *1000  一个小时
    //设置秘钥明文
    public static final String JWT_KEY = "unsoft";

    public static String getUUID(){
        String token = UUID.randomUUID().toString().replaceAll("-", "");
        return token;
    }
    
    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @return
     */
    public static String createJWT(String subject) {
        JwtBuilder builder = getJwtBuilder(subject, null, getUUID());// 设置过期时间
        return builder.compact();
    }

    /**
     * 生成jtw
     * @param subject token中要存放的数据(json格式)
     * @param ttlMillis token超时时间
     * @return
     */
    public static String createJWT(String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, getUUID());// 设置过期时间
        return builder.compact();
    }

    private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        SecretKey secretKey = generalKey();
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        return Jwts.builder()
                .setId(uuid)              //唯一的ID
                .setSubject(subject)   // 主题  可以是JSON数据
                .setIssuer("sg")     // 签发者
                .setIssuedAt(now)      // 签发时间
                .signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥
                .setExpiration(expDate);
    }

    /**
     * 创建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {
        JwtBuilder builder = getJwtBuilder(subject, ttlMillis, id);// 设置过期时间
        return builder.compact();
    }

    public static void main(String[] args) throws Exception {
        String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiJjYWM2ZDVhZi1mNjVlLTQ0MDAtYjcxMi0zYWEwOGIyOTIwYjQiLCJzdWIiOiJzZyIsImlzcyI6InNnIiwiaWF0IjoxNjM4MTA2NzEyLCJleHAiOjE2MzgxMTAzMTJ9.JVsSbkP94wuczb4QryQbAke3ysBDIL5ou8fWsbt_ebg";
        Claims claims = parseJWT(token);
        System.out.println(claims);
    }

    /**
     * 生成加密后的秘钥 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }
    
    /**
     * 解析
     *
     * @param jwt
     * @return
     * @throws Exception
     */
    public static Claims parseJWT(String jwt) throws Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
                .setSigningKey(secretKey)
                .parseClaimsJws(jwt)
                .getBody();
    }


}

 

 

RedisCache

Redis 的操作封装,支持部分Redis操作,未来有新的操作需求可以加到里面

package cn.unsoft.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.TimeUnit;

@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisCache {
    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key   缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key      缓存的键值
     * @param value    缓存的值
     * @param timeout  时间
     * @param timeUnit 时间颗粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(key, timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key     Redis键
     * @param timeout 超时时间
     * @param unit    时间单位
     * @return true=设置成功;false=设置失败
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(key, timeout, unit);
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }

    /**
     * 删除单个对象
     *
     * @param key
     */
    public boolean deleteObject(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return
     */
    public long deleteObject(final Collection collection) {
        return redisTemplate.delete(collection);
    }

    /**
     * 缓存List数据
     *
     * @param key      缓存的键值
     * @param dataList 待缓存的List数据
     * @return 缓存的对象
     */
    public <T> long setCacheList(final String key, final List<T> dataList) {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }

    /**
     * 获得缓存的list对象
     *
     * @param key 缓存的键值
     * @return 缓存键值对应的数据
     */
    public <T> List<T> getCacheList(final String key) {
        return redisTemplate.opsForList().range(key, 0, -1);
    }

    /**
     * 缓存Set
     *
     * @param key     缓存键值
     * @param dataSet 缓存的数据
     * @return 缓存数据的对象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext()) {
            setOperation.add(it.next());
        }
        return setOperation;
    }

    /**
     * 获得缓存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key) {
        return redisTemplate.opsForSet().members(key);
    }

    /**
     * 缓存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap) {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }

    /**
     * 获得缓存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 往Hash中存入数据
     *
     * @param key   Redis键
     * @param hKey  Hash键
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value) {
        redisTemplate.opsForHash().put(key, hKey, value);
    }

    /**
     * 获取Hash中的数据
     *
     * @param key  Redis键
     * @param hKey Hash键
     * @return Hash中的对象
     */
    public <T> T getCacheMapValue(final String key, final String hKey) {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }

    /**
     * 删除Hash中的数据
     *
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey) {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }

    /**
     * 获取多个Hash中的数据
     *
     * @param key   Redis键
     * @param hKeys Hash键集合
     * @return Hash对象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }

    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Collection<String> keys(final String pattern) {
        return redisTemplate.keys(pattern);
    }

    /**
     * 对Redis中的Map数据进行自增操作
     *
     * @param hash Redis 中定义的 Hash值
     * @param key Map 中的 key 值
     * @param v 自增迭代量
     */
    public void incrementCacheMapValue(String hash, String key, int v) {
        redisTemplate.boundHashOps(hash).increment(key, v);
    }
}

 

 

ResponseResult

用于封装JSON返回数据的包装类,所有返回给前端都统一使用该类先进行包装

package cn.unsoft.utils;

import cn.unsoft.utils.enums.AppHttpCodeEnum;
import com.fasterxml.jackson.annotation.JsonInclude;

import java.io.Serializable;

@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseResult<T> implements Serializable {
    private Integer code;
    private String msg;
    private T data;

    public ResponseResult() {
        this.code = AppHttpCodeEnum.SUCCESS.getCode();
        this.msg = AppHttpCodeEnum.SUCCESS.getMsg();
    }

    public ResponseResult(Integer code, T data) {
        this.code = code;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public ResponseResult(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public static ResponseResult errorResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.error(code, msg);
    }
    public static ResponseResult okResult() {
        ResponseResult result = new ResponseResult();
        return result;
    }
    public static ResponseResult okResult(int code, String msg) {
        ResponseResult result = new ResponseResult();
        return result.ok(code, null, msg);
    }

    public static ResponseResult okResult(Object data) {
        ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getMsg());
        if(data!=null) {
            result.setData(data);
        }
        return result;
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums){
        return setAppHttpCodeEnum(enums,enums.getMsg());
    }

    public static ResponseResult errorResult(AppHttpCodeEnum enums, String msg){
        return setAppHttpCodeEnum(enums,msg);
    }

    public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
        return okResult(enums.getCode(),enums.getMsg());
    }

    private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String msg){
        return okResult(enums.getCode(),msg);
    }

    public ResponseResult<?> error(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data) {
        this.code = code;
        this.data = data;
        return this;
    }

    public ResponseResult<?> ok(Integer code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        return this;
    }

    public ResponseResult<?> ok(T data) {
        this.data = data;
        return this;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer 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;
    }



}

 

 

SecurityUtils

当系统引入Spring Security功能后,对于用户的登陆,后端会产生一个 UserDetails 的实现类对象,该工具类是用于在任何地方获取到当前请求的 UserDetails 用户类

package cn.unsoft.utils;

import cn.unsoft.domain.entity.LoginUser;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

/**
 * 用户ID工具类
 * 用于获取用户的 UserId 或 LoginUser 对象
 */
public class SecurityUtils {

    /**
     * 取得当前登陆用户
     *
     * @return
     */
    public static Long getUserId() {
        return getLoginUser().getUser().getId();
    }

    /**
     * 获取当前账号的 LoginUser 对象
     *
     * @return
     */
    public static LoginUser getLoginUser() {
        return (LoginUser) getAuthentication().getPrincipal();
    }

    /**
     * 获取当前账号的 Authentication 对象
     *
     * @return
     */
    public static Authentication getAuthentication() {
        return SecurityContextHolder.getContext().getAuthentication();
    }

    /**
     * 判断是否为管理员
     *
     * @return
     */
    public static boolean isAdmin() {
        Long userId = getUserId();
        return userId != null && userId.equals(1L);
    }
}

 

 

LoginUser

实现 UserDetails 接口的自定义实现类,用于Security中包装已登陆成功的user实体类对象。

package cn.unsoft.domain.entity;

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;
import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginUser implements UserDetails {

    private User user;

    // 用于保存该用户允许操作的权限,权限来自于 Menu 表中的 perms
    // 通过请求方法中与该用户的 permissions 中对比是否存在
    // 可以判断出 该请求方法是否允许该登陆用户访问
    private List<String> permissions;

    @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;
    }
}

 

 

 

UploadUtils

用于把文件上传到【七牛云】平台的工具类,上传完成后,会返回上传链接

url地址前缀需要在yml文件中指定

package cn.unsoft.utils;

import com.google.gson.Gson;
import com.qiniu.common.QiniuException;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.Region;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

/**
 * 用于处理七牛云OSS对象存储功能的工具类
 */
@Component
@Data
@ConfigurationProperties("oss")
public class UploadUtils {

    private String accessKey;
    private String secretKey;
    private String bucket;

    private String domain;

    /**
     * 使用七牛云进行文件上传
     * 使用 数据流 方式上传文件
     *
     * @return
     */


    public String upload(InputStream inputStream, String fileName) {

        //构造一个带指定 Region 对象的配置类
        Configuration cfg = new Configuration(Region.autoRegion());
        cfg.resumableUploadAPIVersion = Configuration.ResumableUploadAPIVersion.V2;// 指定分片上传版本
        //...其他参数参考类注释
        UploadManager uploadManager = new UploadManager(cfg);
        //...生成上传凭证,然后准备上传

        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = generateFilePath(fileName);
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);

        try {
            Response response = uploadManager.put(inputStream, key, upToken, null, null);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            return domain + putRet.key;

        } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
            } catch (QiniuException ex2) {
                //ignore
            }
        }
        return null;
    }

    /**
     * 生成UUID随机名称
     * 目录 yyyy/MM/dd/uuid.xxx
     *
     * @param fileName
     * @return
     */
    public String generateFilePath(String fileName) {
        //根据日期生成路径   2022/1/15/
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd/");
        String datePath = sdf.format(new Date());
        //uuid作为文件名
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        //后缀和文件后缀一致
        int index = fileName.lastIndexOf(".");
        // test.jpg -> .jpg
        String fileType = fileName.substring(index);
        return new StringBuilder().append(datePath).append(uuid).append(fileType).toString();
    }
}

 

 

yml中指定【七牛云】服务参数

# 配置自定义属性参数
oss:
  accessKey: # 七牛云ak码
  secretKey: # 七牛云sk码
  bucket: un-blog # 七牛云存储对象名
  domain: "http://rszvkzqrf.hn-bkt.clouddn.com/" # OSS图片服务器域名

 

 

WebUtils

用于某些场境下,响应请求不在@ResponseBody的环境中,不能自动把Bean转换成JSON对象时,可以通过该类创建JSON返回。

package cn.unsoft.utils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class WebUtils
{
    /**
     * 将字符串渲染到客户端
     * 
     * @param response 渲染对象
     * @param string 待渲染的字符串
     * @return null
     */
    public static void renderString(HttpServletResponse response, String string) {
        try
        {
            response.setStatus(200);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            response.getWriter().print(string);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /**
     * 设置下载文件请求
     * @param response
     */
    public static void downloadFileHandler(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码
        String name = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + name);
    }
}

 

 

配置文件类

FastJsonRedisSerializer

用于使用 FastJson 在 Redis中的序列化工具

package cn.unsoft.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson.parser.ParserConfig;
import org.springframework.util.Assert;
import java.nio.charset.Charset;

/**
 * Redis使用FastJson序列化
 * 
 * @author sg
 */
public class FastJsonRedisSerializer<T> implements RedisSerializer<T>
{

    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    private Class<T> clazz;

    static
    {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }


    protected JavaType getJavaType(Class<?> clazz)
    {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}

 

MyBatisPlusConfig

MyBatisPlus的基本配置Bean

package cn.unsoft.config;

import cn.unsoft.utils.SecurityUtils;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionManager;

import javax.sql.DataSource;
import java.util.Date;

/**
 * MyBatisPlus 配置类项
 * MybatisPlusInterceptor 用于创建一个分页插件,提供分页功能
 * <p>
 * insertFill 是 MyBatisPlus 提供的字段填充功能,可以填充插入数据库时,某些字段自动填充
 * updateFill 是 MyBatisPlus 提供的字段填充功能,可以填充更新数据库时,某些字段自动填充
 * 自动填充,需要在实体类的成员中加上 @TableField(fill) 注解
 */
@Configuration
public class MyBatisPlusConfig implements MetaObjectHandler {

    /**
     * 3.4.0之后版本
     *
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }

    /**
     * 对某些字段设定自动数据填充功能
     * @param metaObject 由 MyBatisPlus 提供的当前正在处理的数据行中的元数据
     *                   包含了当前正在处理的数据行的实体类对象等信息
     *                   包含了处理该数据行的 条件 信息
     *                   可以在这个metaObject对象中处理需要加工的字段
     *                   当metaObject被提交出去之后,MP 将按照 metaObject中
     *                   所提供的实体类数据进行数据库写入操作
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        Long userId = null;
        try {
            // 如果取不到UserId,说明这个是一个注册账号的请求
            userId = SecurityUtils.getUserId();
        } catch (Exception e) {
            e.printStackTrace();
            userId = -1L;//表示是自己创建
        }
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("createBy", userId, metaObject);
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("updateBy", userId, metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", new Date(), metaObject);
        this.setFieldValByName("updateBy", SecurityUtils.getUserId(), metaObject);
    }


    /**
     * 配置数据库事务管理器
     */
    @Autowired
    private DataSource dataSource;
    @Bean
    public TransactionManager transactionManager(){
        return new DataSourceTransactionManager(dataSource);
    }
}

 

RedisConfig

Redis 的基本配置Bean

package cn.unsoft.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

    @Bean
    @SuppressWarnings(value = { "unchecked", "rawtypes" })
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
    {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);

        FastJsonRedisSerializer serializer = new FastJsonRedisSerializer(Object.class);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(serializer);

        // Hash的key也采用StringRedisSerializer的序列化方式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(serializer);

        template.afterPropertiesSet();
        return template;
    }
}

 

WebConfig

配置基本的Web设置,如Date时间转JSON的格式设置,和关闭跨域问题。

package cn.unsoft.config;

import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.ToStringSerializer;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 关闭CORS跨域问题
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        // 设置允许跨域的路径
        registry.addMapping("/**")
                 // 设置允许跨域请求的域名
                .allowedOriginPatterns("*")
                // 是否允许cookie
                .allowCredentials(true)
                // 设置允许的请求方式
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                // 设置允许的header属性
                .allowedHeaders("*")
                // 跨域允许时间
                .maxAge(3600);
    }

    /**
     * 注入fastJsonHttpMessageConvert
     * 用于解决 Date 类型转成 JSON 时的格式不正确问题
     * @return
     */
    @Bean
    public HttpMessageConverter fastJsonHttpMessageConverters() {
        //1.需要定义一个Convert转换消息的对象
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);
        fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss");

        // 把所有 Long 类型的数据都转换成 String 数据
        SerializeConfig.globalInstance.put(Long.class, ToStringSerializer.instance);

        fastJsonConfig.setSerializeConfig(SerializeConfig.globalInstance);
        fastConverter.setFastJsonConfig(fastJsonConfig);
//        HttpMessageConverter<?> converter = fastConverter;
        return fastConverter;
    }

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(fastJsonHttpMessageConverters());
    }
}

 

切面日志类

LogAspect

用于对某些请求进行日志监控的切面类

package cn.unsoft.aop;

import cn.unsoft.annotation.SystemLog;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@Component
@Aspect
@Slf4j
public class LogAspect {

    /**
     * 配置一个切面定位
     * 使用 @annotation 定义,则所有使用了 cn.unsoft.annotation.SystemLog 注解的方法
     * 都会被包装成一个 aop 切面类
     */
    @Pointcut("@annotation(cn.unsoft.annotation.SystemLog)")
    public void pointcut() {
    }

    /**
     * 配置触发切面的处理方法
     * 难点+重点:
     * 1.在切面方法中,需要获取到接入点(被切入)的注册对象,从而获取切面信息 (另开一个方法获取)
     * 2.需要获取到该接入点收到的请求对象,从而获取请求信息 (另开一个方法获取)
     */
    @Around("pointcut()")
    public void log(ProceedingJoinPoint point) throws Throwable {
        Object result = null;
        SystemLog systemLog = getSystemLogAnno(point);
        HttpServletRequest request = getRequestContext();
        MethodSignature methodSignature = (MethodSignature) point.getSignature();

        log.info("=======Start=======");
        try {
            result = point.proceed();
            // 打印请求 URL
            log.info("URL            : {}", request.getRequestURL());
            // 打印描述信息
            log.info("BusinessName   : {}", systemLog.businessName());
            // 打印 Http method
            log.info("HTTP Method    : {}", request.getMethod());
            // 打印调用 controller 的全路径以及执行方法
            log.info("Class Method   : {}.{}", point.getSignature().getDeclaringTypeName(), methodSignature.getName());
            // 打印请求的 IP
            log.info("IP             : {}", request.getRemoteHost());
            // 打印请求入参
            log.info("Request Args   : {}", JSON.toJSONString(point.getArgs()));
            // 打印出参
            log.info("Response       : {}", JSON.toJSONString(result));
        } finally {
            // 结束后换行
            log.info("=======End=======" + System.lineSeparator());
        }


    }

    /**
     * 获取到请求对象
     * 重点:
     * 本方法,需要通过 @Around 提供的切面点对象,来获取 HttpServletRequest 的对象
     * <p>
     * 解释:
     * 1.可以通过 RequestContextHolder 来获取当前请求的对象,
     * RequestContextHolder 是一个线程安全的全局请求池,在里面可以获取到当前请求的一些相关信息
     * <p>
     * 2.所获得的 RequestAttributes 对象,是一个接口类型,实现这个接口的实现类非常多,我们可以通过
     * 强转成 ServletRequestAttributes 实现类,来获取 HttpServletRequest 对象,因为它包含了
     * HttpServletRequest 对象
     *
     * @return
     */
    private HttpServletRequest getRequestContext() {
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        return requestAttributes.getRequest();
    }

    /**
     * 获取到注解对象
     * 重点:
     * 本方法,需要通过 @Around 提供的切面点对象,来获取接入点的注解
     * <p>
     * 解释:
     * 1.通过 ProceedingJoinPoint 的 getSignature 方法可以获取到接入点的签名信息
     * Signature 对象,但是 Signature 是一个接口,而 Signature 接口有多种实现方法
     * 因为我们注解使用到方法中,所以可以强转为 MethodSignature 实现类。
     * <p>
     * 2.通过 MethodSignature 实现类,我们可以获取到详细的与方法有关的信息,如方法名,方法上方的注解
     * 我们就可以获取到方法上面的对应注解对象
     *
     * @return
     */
    private SystemLog getSystemLogAnno(ProceedingJoinPoint point) {
        MethodSignature method = (MethodSignature) point.getSignature();
        SystemLog annotation = method.getMethod().getAnnotation(SystemLog.class);
        return annotation;
    }
}

 

字面量规范类

SystemConstant

可以把一些字面量定义为一个常量,可以在开发中规范字面量的含意或语义化,可按需求增加

package cn.unsoft.constants;

public class SystemConstant {

    public static final String TOKEN = "token";
}

 

异常处理类

GlobalExceptionHandler

用于处理除已知异常以处的所有其它异常的处理定义

package cn.unsoft.exception.system;

import cn.unsoft.utils.ResponseResult;
import cn.unsoft.utils.enums.AppHttpCodeEnum;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
 * 设置异常处理 AOP 类
 * 本类是用于接收当系统中发生抛出异常时的处理类
 * 当系统发生抛出异常时,系统全根据 ExceptionHandler 处理对应异常的方法
 */
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 处理自定义抛出的异常类
     *
     * @param e
     * @return
     */
    @ExceptionHandler(SystemException.class)
    public ResponseResult systemException(SystemException e) {
        ResponseResult result = ResponseResult.errorResult(e.getCode(), e.getMsg());
        return result;
    }


    /**
     * 处理系统全局异常抛出
     * 除了指定定义的异常处,其它异常的抛出都会触发这个方法
     *
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseResult exception(Exception e) {
        return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(), e.getMessage());
    }
}

 

SystemException

用于自己调用的经过格式封装后的异常类,即如果在开发过程中知道某些地方可能出现异常的地方,可以用于调用它作为返回异常给前端的异常类

package cn.unsoft.exception.system;

import cn.unsoft.utils.enums.AppHttpCodeEnum;

/**
 * 用于自定义异常的异常类
 * 这个类可以用在系统需要抛出异常的地方,就可以使用
 * throw new SystemException(AppHttpCodeEnum.xxx)
 */

public class SystemException extends RuntimeException{

    private Integer code;
    private String msg;

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public SystemException(AppHttpCodeEnum httpCodeEnum) {
        super(httpCodeEnum.getMsg());
        this.code = httpCodeEnum.getCode();
        this.msg = httpCodeEnum.getMsg();
    }
}

 

请求过滤器

JwtAuthenticationTokenFilter

当用于携带着由jwt生成的token字串过来时,会经过这个过滤器,这个过滤器被按排在 usernamepassword 过滤器之前,用于拦截请求并判断用户是否已登陆,如果登陆了,则在Redis中取出该UserDetails对象,并识别授权。

package cn.unsoft.filter;

import cn.unsoft.constants.SystemConstant;
import cn.unsoft.domain.entity.LoginUser;
import cn.unsoft.utils.JwtUtil;
import cn.unsoft.utils.RedisCache;
import cn.unsoft.utils.ResponseResult;
import cn.unsoft.utils.WebUtils;
import cn.unsoft.utils.enums.AppHttpCodeEnum;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * 获取token进行验证的过滤器
     * 注意:这是一个过滤器,定义完过滤器后,应该要把过滤器加到请求控制中
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */

    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = httpServletRequest.getHeader(SystemConstant.TOKEN);
        // 如果没有,则直接放行
        if (!StringUtils.hasText(token)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        // 如果有,则先jwt解密
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            // 如果jwt解密有误
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
            return;
        }

        // 如果jwt解密成功
        String userid = claims.getSubject();
        // 在Redis中取出 LoginUser
        LoginUser loginUser = redisCache.getCacheObject("admin::login::" + userid);
        if (Objects.isNull(loginUser)) {
            // 用户在Redis中不存在,有可能过时了。
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
            return;
        }

        // 如果Redis中存在LoginUser,把LoginUser存到 SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 处理完token之后一定要放行
        filterChain.doFilter(httpServletRequest, httpServletResponse);


    }
}

 

Security 配置类

SpringSecurityConfig

对于使用SpringSecurity模块的Bean配置类,用于配置授权异常,哪些api接口需要登陆才能访问等配置

package cn.unsoft.config;

import cn.unsoft.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
// 开启用户权限控制制度,开启后就可以使用 @PreAuthorize() 对请求方法进行权限鉴权操作
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    // 针对前端请求的验证过滤配置
    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

    @Autowired
    private AuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private AccessDeniedHandler accessDeniedHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        String[] anonymousMatchers = {
                "/user/login"
        };

        // 需要验证才能访问的请求 url
        String[] authMatchers = {
                ""
        };

        http
                //关闭csrf
                .csrf().disable()
                //不通过Session获取SecurityContext
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 对于登录接口 允许匿名访问
                .antMatchers(anonymousMatchers).anonymous()
                // 对于注销接口,需要登陆权限后才能访问
//                .antMatchers(authMatchers).authenticated()
                // 除上面外的所有请求全部不需要认证即可访问
                .anyRequest().authenticated();

        // 把验证 token 的过滤器加到请求过滤中,而且是加到 usernamepass 用户登陆请求之前
        // 当然,因为没有调用 super.configure() 所以没有加载 usernamepass 用户登陆的模块
        http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);

        // 把认证异常加到请求配置中
        http.exceptionHandling()
                .authenticationEntryPoint(authenticationEntryPoint)
                .accessDeniedHandler(accessDeniedHandler);

        // 关闭默认注销功能
        http.logout().disable();
        //允许跨域
        http.cors();

    }

    /**
     * 定义密码加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

 

 

Security 异常类

AccessDeniedHandlerImpl

对于用户已成功登陆,却访问他不该访问的api请求时,所抛出的异常处理。

package cn.unsoft.filter;

import cn.unsoft.constants.SystemConstant;
import cn.unsoft.domain.entity.LoginUser;
import cn.unsoft.utils.JwtUtil;
import cn.unsoft.utils.RedisCache;
import cn.unsoft.utils.ResponseResult;
import cn.unsoft.utils.WebUtils;
import cn.unsoft.utils.enums.AppHttpCodeEnum;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    /**
     * 获取token进行验证的过滤器
     * 注意:这是一个过滤器,定义完过滤器后,应该要把过滤器加到请求控制中
     *
     * @param httpServletRequest
     * @param httpServletResponse
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */

    @Autowired
    private RedisCache redisCache;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String token = httpServletRequest.getHeader(SystemConstant.TOKEN);
        // 如果没有,则直接放行
        if (!StringUtils.hasText(token)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        // 如果有,则先jwt解密
        Claims claims = null;
        try {
            claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            // 如果jwt解密有误
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
            return;
        }

        // 如果jwt解密成功
        String userid = claims.getSubject();
        // 在Redis中取出 LoginUser
        LoginUser loginUser = redisCache.getCacheObject("admin::login::" + userid);
        if (Objects.isNull(loginUser)) {
            // 用户在Redis中不存在,有可能过时了。
            ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
            return;
        }

        // 如果Redis中存在LoginUser,把LoginUser存到 SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        // 处理完token之后一定要放行
        filterChain.doFilter(httpServletRequest, httpServletResponse);


    }
}

 

AuthenticationEntryPointImpl

对于用户登陆不成功时抛出的异常处理。

package cn.unsoft.exception.security;

import cn.unsoft.utils.ResponseResult;
import cn.unsoft.utils.WebUtils;
import cn.unsoft.utils.enums.AppHttpCodeEnum;
import com.alibaba.fastjson.JSON;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * SpringSecurity 认证失败时的异常处理方法
 * 认证失败是指用户账号密码不正确时所产生的异常处理,Spring 会调用 AuthenticationEntryPoint 接口的实现方法
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint {

    /**
     * 当用户登陆失败,或访问需要登陆的请求时而没有必需的token时,会触发
     * (就是jwt解密token验证类中抛出异常时的调用)
     * @param httpServletRequest
     * @param httpServletResponse
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        ResponseResult result = null;
        if(e instanceof BadCredentialsException){
            // 用户账号密码登陆错误时的异常
            result = ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_ERROR.getCode(),e.getMessage());
        }else if(e instanceof InsufficientAuthenticationException){
            // 用户访问需要登陆才能访问的接口时的异常
            result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }else{
            // 其它异常
            result = ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR.getCode(),"认证或授权失败");
        }

        WebUtils.renderString(httpServletResponse, JSON.toJSONString(result));
    }
}

 

YML常用配置

server:
  port: 7777
spring:
  datasource:
    druid: # 数据源
      url: jdbc:mysql://localhost:3306/un_blog?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
      username: root
      password: tzminglove
      driver-class-name: com.mysql.cj.jdbc.Driver
  servlet:  # 文件上传配置
    multipart:
      max-file-size: 2MB  # 最大文件大小
      max-request-size: 5MB # 指定每次请求的最大值,默认为10MB
      enabled: true # 开启文件上传
  redis:
    host: localhost
    username:
    password:
    port: 6379
mybatis-plus: # mp配置
  configuration:
    # 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
      logic-delete-field: delFlag # 逻辑删除的字段
      logic-delete-value: 1 # 删除了的时候的值
      logic-not-delete-value: 0 # 没有删除的时候的值
      id-type: auto # 设置自增ID规则
  mapper-locations: classpath*:/mapper/**/*.xml

# 配置自定义属性参数
oss:
  accessKey: # 七牛云ak码
  secretKey: # 七牛云sk码
  bucket: un-blog # 七牛云存储对象名
  domain: "http://rszvkzqrf.hn-bkt.clouddn.com/" # OSS图片服务器域名

 

整体参考项目下载:

 

如果您喜欢本站,点击这儿不花一分钱捐赠本站

这些信息可能会帮助到你: 下载帮助 | 报毒说明 | 进站必看

修改版本安卓软件,加群提示为修改者自留,非本站信息,注意鉴别

THE END
分享
二维码
打赏
海报
Java – SpringBoot基础项目 – UnBlog 博客
常用项目工具类下载: AppHttpCodeEnum 用于定义WEB服务中出现各种问题的枚举类,可以规范项目中的错误表达方式。 package cn.unsoft.utils.enums; public enum AppHttpCodeEnum { //……
<<上一篇
下一篇>>