使用mybatisplus快速开发springboot项目(一)-CSDN博客
使用mybatisplus快速开发springboot项目(二)_如何用mybatis-plus写业务-CSDN博客
springboot中redis配置
pom.xml导入依赖
<!--集成redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--开启redis缓存注解-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
application.yml配置文件
spring:
redis:
host: 127.0.0.1
port: 6379
RedisConfig.java配置文件
package com.example.examservice.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
@Configuration
@EnableCaching//允许我们使用缓存
public class RedisConfig {
/**
* 缓存过期时间(秒)
*/
public static final long CACHE_EXPIRE_SECEND = 3600 * 2;
@Bean//此时,将我们的redisTemplate加载到了我们的spring的上下文中,applicationContext
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
//初始化一个redisTemplate
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<String,Object>();
//序列话(一般用于key值)
RedisSerializer<String> redisSerializer=new StringRedisSerializer();
//引入json串的转化类(一般用于value的处理)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper=new ObjectMapper();
//设置objectMapper的访问权限
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//指定序列化输入类型,就是将数据库里的数据按照一定类型存储到redis缓存中。
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//创建链接
redisTemplate.setConnectionFactory(factory);
//redis key值序列化
redisTemplate.setKeySerializer(redisSerializer);
//value序列化,因为我们的value大多是通过对象转化过来的,所以使用jackson2JsonRedisSerializer
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
//value序列化,hashmap的序列话
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
return redisTemplate;
}
@Bean
public CacheManager cacheManager(RedisConnectionFactory factory){
//序列化(一般用于key值)
RedisSerializer<String> redisSerializer=new StringRedisSerializer();
//引入json串的转化类(一般用于value的处理)
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer=new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper=new ObjectMapper();
//设置objectMapper的访问权限
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//指定序列化输入类型,就是将数据库里的数据按照一定类型存储到redis缓存中。
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
//序列话配置,乱码问题解决以及我们缓存的时效性
RedisCacheConfiguration config=RedisCacheConfiguration.defaultCacheConfig().
entryTtl(Duration.ofSeconds(CACHE_EXPIRE_SECEND)).//缓存时效性设置
serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).//key序列化
serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).//value序列化
disableCachingNullValues();//空值不存入缓存
//创建cacheManager链接并设置属性
RedisCacheManager cacheManager= RedisCacheManager.builder(factory).cacheDefaults(config).build();
return cacheManager;
}
}
BaseContext.java(本地线程静态方法,可忽略)
package com.example.examservice.common;
/**
* 基于ThreadLocal封装工具类,用户保存和获取当前登录用户id。 每个线程都有一个单独的存储空间,线程之间隔离
*/
public class BaseContext {
private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
/**
* 设置值
* @param id
*/
public static void setCurrentId(Long id){
threadLocal.set(id);
}
/**
* 获取值
* @return
*/
public static Long getCurrentId(){
return threadLocal.get();
}
}
springboot中jwt配置
pom.xml导入依赖
<!-- jwt -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
JWTUtils.java(jwt工具类)
package com.example.examservice.utils;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Map;
/**
* @Description: JWT工具
*/
public class JWTUtils {
// 签名密钥
private static final String SECRET = "!DAR$";
/**
* 生成token
* @param payload token携带的信息
* @return token字符串
*/
public static String getToken(Map<String,String> payload){
// 指定token过期时间为60分钟
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MINUTE, 180);
JWTCreator.Builder builder = JWT.create();
// 构建payload
payload.forEach((k,v) -> builder.withClaim(k,v));
// 指定过期时间和签名算法
String token = builder.withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(SECRET));
return token;
}
/**
* 解析token
* @param token token字符串
* @return 解析后的token
*/
public static DecodedJWT decode(String token){
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(SECRET)).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
return decodedJWT;
}
/**
* 获取jwt中的token
* @Param [request]
* @return 返回redis中的token键
*/
public static String getTokenByJWT(HttpServletRequest request){
String token = request.getHeader("Authorization");//获取Authorization请求头中的jwt信息
DecodedJWT decode = decode(token);//解析jwt信息
String Token = decode.getClaim("token").asString();//获取jwt中的token信息并转为String
return Token;
}
}
JWTInterceptor.java(jwt拦截器类)
package com.example.examservice.interceptor;
import com.example.examservice.common.BaseContext;
import com.example.examservice.utils.JWTUtils;
import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import com.auth0.jwt.interfaces.DecodedJWT;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Slf4j
public class JWTInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String JWT = request.getHeader("Authorization");
try {
//对跨域中的预检请求进行放行
if("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
// 1.校验JWT字符串
DecodedJWT decodedJWT = JWTUtils.decode(JWT);
// 2.取出JWT字符串载荷中的用户信息
String userId = decodedJWT.getClaim("id").asString();
log.info("当前线程的id:"+userId);
BaseContext.setCurrentId(Long.valueOf(userId));
return true;
}catch (SignatureVerificationException e){
response.setStatus(508);
System.out.println("无效签名");
e.printStackTrace();
}catch (TokenExpiredException e){
response.setStatus(514);
System.out.println("token已经过期");
e.printStackTrace();
}catch (AlgorithmMismatchException e){
response.setStatus(508);
System.out.println("算法不一致");
e.printStackTrace();
}catch (Exception e){
response.setStatus(508);
System.out.println("token无效");
e.printStackTrace();
}
return false;
}
}
WebMvcConfig.java(springbootMVC配置文件)
package com.example.examservice.config;
import com.example.examservice.common.JacksonObjectMapper;
import com.example.examservice.interceptor.JWTInterceptor;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.util.List;
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
/**
* 扩展mvc框架的消息转换器
* @param converters
*/
@Override
protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
// 创建消息转换器对象
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
// 设置对象转换器,底层使用Jackson将Java对象转为json
messageConverter.setObjectMapper(new JacksonObjectMapper());
// 将上面的消息转换器对象追加到mvc框架的转换器集合中
converters.add(0, messageConverter);
}
/**
* 跨域配置
* @Param [registry]
*/
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 所有接口
.allowCredentials(true) // 是否发送 Cookie
.allowedOriginPatterns("*") // 支持域
.allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE","PATCH"}) // 支持方法
.allowedHeaders("*")
.exposedHeaders("*");
}
/**
* 注册jwt拦截器
* @Param [registry]
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new JWTInterceptor()).
excludePathPatterns("/user/login", "/user/info", "/question/**").//登录请求放行
addPathPatterns("/**");
}
}
springboot登录业务实现
返回通用类R参考之前文章,这里直接给出三个具体实现业务,登录,获取用户登录用户信息,登出。
package com.example.examservice.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.examservice.common.CustomException;
import com.example.examservice.common.R;
import com.example.examservice.entity.ClassStudent;
import com.example.examservice.entity.User;
import com.example.examservice.mapper.UserMapper;
import com.example.examservice.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.examservice.utils.JWTUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 用户信息 服务实现类
* </p>
*
* @author liujianchen
* @since 2024-02-19
*/
@Transactional
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private RedisTemplate redisTemplate;
@Override
public R login(User user) {
//将页面提交的密码password进行md5加密处理
String password = user.getPassword();
password = DigestUtils.md5DigestAsHex(password.getBytes());
//根据页面提交的用户名username查询数据库
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getUsername,user.getUsername());
User one = this.getOne(queryWrapper);
//如果没有查询到则返回登录失败结果
if(one == null){
return R.error().message("账号不存在");
}
//密码比对,如果不一致则返回登录失败结果
if(!one.getPassword().equals(password)){
return R.error().message("密码错误");
}
//查看员工状态,如果为已禁用状态,则返回员工已禁用结果
if(one.getStatus() == 0){
return R.error().message("账号已禁用");
}
// 登录成功,UUID随机生成唯一Token存入redis中,实现登出
String Token ="employee_" + UUID.randomUUID().toString().replaceAll("-", "");
redisTemplate.opsForValue().set(Token,one,180, TimeUnit.MINUTES);
//用jwt生成token
Map<String, String> payload = new HashMap<>();
payload.put("id", String.valueOf(one.getId()));
payload.put("token", Token);//存入redis中的Token,作为关联
String token = JWTUtils.getToken(payload);
return R.ok().data("token",token);
}
@Override
public R info(HttpServletRequest request) {
// 获取jwt中redis的token键
String Token = JWTUtils.getTokenByJWT(request);
// redis中是否有该token键
if (!redisTemplate.hasKey(Token)){
throw new CustomException("用户信息已过期");
}
User user = (User) redisTemplate.opsForValue().get(Token);
return R.ok().data("name",user.getName()).data("roles",user.getRoles()).data("id",user.getId())
.data("avatar",user.getAvatar());
}
@Override
public R logout(HttpServletRequest request) {
// 删除redis缓存中的用户信息
redisTemplate.delete(JWTUtils.getTokenByJWT(request));
return R.ok();
}
}
request.js(前端拦截器)
为每个向后端发送的请求设置请求头给后端jwt解析,请求头是键值对的结构即("Authorization", token)的格式。后端设置传参HttpServletRequest request
通过request.getHeader("Authorization")拿到token
import axios from "axios";
import { MessageBox, Message, Notification } from "element-ui";
import store from "@/store";
import { getToken } from "@/utils/auth";
import defaultSettings from "@/settings";
const url = defaultSettings.url || "http://localhost";
const post = defaultSettings.post || "9000";
const service = axios.create({
baseURL: url + ":" + post,
timeout: 5000,
});
// request interceptor
service.interceptors.request.use(
(config) => {
if (store.getters.token) {
//token保存的请求头
config.headers["Authorization"] = getToken();
}
return config;
},
(error) => {
console.log(error); // for debug
return Promise.reject(error);
}
);
// response interceptor
service.interceptors.response.use(
(response) => {
const res = response.data;
console.log("状态码" + response.status);
if (res.code !== 200) {
Notification.error({
title: "请求错误",
message: res.message || "Error",
duration: 5 * 1000,
});
return Promise.reject(new Error(res.message || "Error"));
} else {
return res;
}
},
(error) => {
console.log("err:" + error); // for debug
console.log(error);
let { message } = error;
if (message == "Network Error") {
message = "后端接口连接异常";
} else if (message.includes("timeout")) {
message = "系统接口请求超时";
} else if (message.includes("Request failed with status code")) {
message = "系统接口" + message.substr(message.length - 3) + "异常";
}
Notification.error({
title: "获取失败",
message: "请重新登录",
duration: 5 * 1000,
});
const err = error.response.status;
// 50008:非法的token; 50012:其他客户端登录了; 50014:Token 过期了;
if (err === 508 || err === 512 || err === 514) {
// to re-login
store.dispatch("user/resetToken").then(() => {
// location.reload(); // 为了重新实例化vue-router对象 避免bug
});
}
return Promise.reject(error);
}
);
export default service;