Serie de artículos:
Práctica del proyecto de separación de front-end y back-end de SpringBoot + Vue || Uno: diseño de front-end de Vue
Práctica del proyecto de separación de front-end de SpringBoot + Vue || Dos: backend de Spring Boot y conexión de base de datos
SpringBoot + Vue front-end práctica del proyecto de separación final || Tres: conexión Spring Boot back-end y Vue front-end
Práctica del proyecto de separación SpringBoot + Vue front-end y back-end || Cuatro: implementación de la función de gestión de usuarios Práctica del proyecto de separación SpringBoot + Vue front-end || Tres: implementación de la función de gestión de usuarios
SpringBoot + Vue práctica del proyecto de separación front-end || Tres: conexión de back-end de Spring Boot y Vue front-end | | Cinco: Seguimiento de la función de gestión de usuarios
Directorio de artículos
Acoplamiento frontal y posterior
Modifique la interfaz del front-end para conectarse al back-end
src\api\user.js
Modifique la dirección de solicitud para mantenerla coherente con el backend
Registre los campos src\utils\request.js
en la parte frontal.X-Token
Cambie la dirección de solicitud en el entorno de desarrollo a la dirección de backendhttp://localhost:9999
Desactivar la anotación del servicio de datos simulados de front-end
Configuración general del backend
Cree un nuevo config
paquete en el backend y cree dos nuevas clases en el paquete
MyCorsConfig
Se utiliza para configurar el acceso asincrónico y conectarse al enlace de acceso front-end"http://localhost:8888"
. Esta configuración se puedeNginx
utilizar en su lugar.MyRedisConfig
Se utiliza para configurar el servicio de serialización de Redis.
MyCorsConfig
El código configurado en es el siguiente:
package com.ums.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class MyCorsConfig {
@Bean
public CorsFilter corsFilter() {
CorsConfiguration configuration = new CorsConfiguration();
// 允许什么网址来异步访问
configuration.addAllowedOrigin("http://localhost:8888");
// 获取cookie
configuration.setAllowCredentials(true);
// 允许什么方法? POST、GET,此处为* 意味全部允许
configuration.addAllowedMethod("*");
// 允许所有的请求头
configuration.addAllowedHeader("*");
// 设置资源过滤器,过滤什么资源
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
MyRedisConfig
El código utilizado para la configuración es el siguiente:
package com.ums.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.TimeZone;
@Configuration
public class MyRedisConfig {
@Resource
private RedisConnectionFactory factory;
@Bean
public RedisTemplate redisTemplate(){
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
// 设置键值序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
redisTemplate.setValueSerializer(serializer);
// 序列化,死代码
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
om.setTimeZone(TimeZone.getDefault());
om.configure(MapperFeature.USE_ANNOTATIONS, false);
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance ,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
serializer.setObjectMapper(om);
return redisTemplate;
}
}
Escriba el código comercial de inicio y cierre de sesión en el backend
El método de solicitud de la interfaz de inicio de sesión del proyecto VUE front-end es el POST
presentado anteriormente
UserController
Agregue código para el control de inicio de sesión
@PostMapping("/login")
public Result<Map<String,Object>> login(@RequestBody User user){
// 因为 user传过来为json字符串,所以用@RequestBody 进行实体转换
// 业务代码在userService里完成
Map<String,Object> data = userService.login(user);
if(data != null){
return Result.success(data,"登录成功");
}
return Result.fail(2002,"用户名或密码错误");
}
Como se muestra en la figura siguiente userService.login()
, el método se volverá popular porque el método no se ha definido ni implementado. En este momento, haga clic con el mouse y presioneAlt+Enter
Seleccione el primer elemento:
IDEA generará automáticamente el código de interfaz
En este momento, hay un mensaje encima de la interfaz 1 related problem
. Al hacer clic con el botón izquierdo del mouse, se saltará al código de implementación de la interfaz.UserServiceImpl
De manera similar, en la definición de toda la clase Alt + Enter
, seleccione la primera, luego seleccione la primera en el cuadro de diálogo emergente y se generará el código.
código generado
Escribe el siguiente código en esta función.
@Override
public Map<String, Object> login(User user) {
// 查询数据库
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, user.getUsername());
wrapper.eq(User::getPassword, user.getPassword());
User loginUser = this.baseMapper.selectOne(wrapper);
// 结果不为空,生成token,将用户信息存入redis
if (loginUser != null) {
// 用UUID,终极方案是jwt
String key = "user:" + UUID.randomUUID();
// 存入redis
loginUser.setPassword(null); // 设置密码为空,密码没必要放入
redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout为登录时间
// 返回数据
Map<String, Object> data = new HashMap<>();
data.put("token",key);
return data;
}
// 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token
// 并将用户信息存入redis
return null;
}
Regresar UserController
, el código es normal en este momento.
Siga el método anterior y escriba el código obtenido y el código
enUserController
token
logout
Entre ellos, estas dos interfaces no están implementadas.
El código se muestra a continuación.
@GetMapping("/info")
public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){
// @RequestParam("token") 是从url中获取值
// 根据token获取用户信息,信息存进了redis中
Map<String,Object> data = userService.getUserInfo(token);
if(data != null){
return Result.success(data);
}
return Result.fail(2003,"登录信息无效,请重新登录");
}
@PostMapping("/logout")
public Result<?> logout(@RequestHeader("X-Token") String token){
userService.logout(token);
return Result.success();
Entonces es hora Alt + Enter
de corregir el error.
Primero UserServiceImpl
define unredisTemplate
Luego UserServiceImpl
escribe el siguiente código en
@Override
public Map<String, Object> getUserInfo(String token) {
// 之前已将对象进行序列化处理存入redis,现在从redis中取出需要反序列化处理
Object obj = redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串
if (obj!= null) {
User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class);
Map<String,Object> data = new HashMap<>();
data.put("name",loginUser.getUsername());
data.put("avatar",loginUser.getAvatar());
// 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口
List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId());
data.put("roles",roleList);
return data;
}
return null;
}
@Override
public void logout(String token) {
redisTemplate.delete(token); // 从redis中删除token
}
Tenga en cuenta que el código en el círculo rojo es una consulta de tabla conjunta. Esta es una SQL
consulta personalizada. A continuación, defínala
Encuentra UserMapper
y escribe el código:
public List<String> getRoleNameByUserId(Integer userId);
Luego vaya resources\mapper\sys\UserMapper.xml
y escriba la declaración SQL en
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ums.sys.mapper.UserMapper">
<select id="getRoleNameByUserId" parameterType="Integer" resultType="String">
SELECT
b.role_name
FROM
x_user_role a,x_role b
WHERE
a.role_id=b.role_id
AND
a.user_id = #{userId}
</select>
</mapper>
prueba
Una vez configurado redis
, Redis se inicia antes de iniciar SpringBoot.
Primero busque el directorio de instalación de redis.
Abra cmd
el directorio y
ejecute el comando redis-server.exe redis.windows.conf
, presione Enter, aparecerá la siguiente interfaz, luego minimice esta ventana, no la cierre
Luego inicia SprinfBoot
el backend
Luego comienza Vue
la parte delantera.
Después de hacer clic para iniciar sesión, podrá ver http://localhost:9999/user/login
el cambio de la dirección de la interfaz.
Los generados por el backend token
también se registran redis
en
Haga clic en 退出登录
, el token en redis también se cancela
Todo el código en el backend
Evite errores al tomar notas y adjunte todos los códigos
UserController
código enpackage com.ums.sys.controller; import com.ums.common.vo.Result; import com.ums.sys.entity.User; import com.ums.sys.service.IUserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import org.springframework.stereotype.Controller; import java.util.List; import java.util.Map; /** * <p> * 前端控制器 * </p> * * @author anthony * @since 2023-06-16 */ @RestController @RequestMapping("/user") // @CrossOrigin //处理跨域,因为前端和后端的IP一致但端口不一致,所以浏览器认为跨域,不给访问,可用Ngx来解决 public class UserController { @Autowired private IUserService userService; @GetMapping("/all") public Result<List<User>> getAllUser() { List<User> list = userService.list(); return Result.success(list,"查询成功"); } @PostMapping("/login") public Result<Map<String,Object>> login(@RequestBody User user){ // 因为 user传过来为json字符串,所以用@RequestBody 进行实体转换 // 业务代码在userService里完成 Map<String,Object> data = userService.login(user); if(data != null){ return Result.success(data,"登录成功"); } return Result.fail(2002,"用户名或密码错误"); } @GetMapping("/info") public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){ // @RequestParam("token") 是从url中获取值 // 根据token获取用户信息,信息存进了redis中 Map<String,Object> data = userService.getUserInfo(token); if(data != null){ return Result.success(data); } return Result.fail(2003,"登录信息无效,请重新登录"); } @PostMapping("/logout") public Result<?> logout(@RequestHeader("X-Token") String token){ userService.logout(token); return Result.success(); } }
mapper\UserMapper
código enpackage com.ums.sys.mapper; import com.ums.sys.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import java.util.List; /** * <p> * Mapper 接口 * </p> * * @author chenhao * @since 2023-06-16 */ public interface UserMapper extends BaseMapper<User> { public List<String> getRoleNameByUserId(Integer userId); }
service\IUserService
código en la interfazpackage com.ums.sys.service; import com.ums.sys.entity.User; import com.baomidou.mybatisplus.extension.service.IService; import java.util.Map; /** * <p> * 服务类 * </p> * * @author chenhao * @since 2023-06-16 */ public interface IUserService extends IService<User> { // Map<String, Object> login(User user); Map<String, Object> getUserInfo(String token); void logout(String token); Map<String, Object> login(User user); }
service\impl\UserServiceImpl
código enpackage com.ums.sys.service.impl; import com.alibaba.fastjson2.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.ums.sys.entity.User; import com.ums.sys.mapper.UserMapper; import com.ums.sys.service.IUserService; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * <p> * 服务实现类 * </p> * * @author chenhao * @since 2023-06-16 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Autowired private RedisTemplate redisTemplate; @Override public Map<String, Object> login(User user) { // 查询数据库 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUsername, user.getUsername()); wrapper.eq(User::getPassword, user.getPassword()); User loginUser = this.baseMapper.selectOne(wrapper); // 结果不为空,生成token,将用户信息存入redis if (loginUser != null) { // 用UUID,终极方案是jwt String key = "user:" + UUID.randomUUID(); // 存入redis loginUser.setPassword(null); // 设置密码为空,密码没必要放入 redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout为登录时间 // 返回数据 Map<String, Object> data = new HashMap<>(); data.put("token",key); return data; } // 结果不为空,生成token,前后端分离,前端无法使用session,可以使用token // 并将用户信息存入redis return null; } @Override public Map<String, Object> getUserInfo(String token) { // 之前已将对象进行序列化处理存入redis,现在从redis中取出需要反序列化处理 Object obj = redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串 if (obj!= null) { User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class); Map<String,Object> data = new HashMap<>(); data.put("name",loginUser.getUsername()); data.put("avatar",loginUser.getAvatar()); // 先在xml里写SQL语句id=getRoleNameByUserId,然后去UserMapper里实现接口 List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId()); data.put("roles",roleList); return data; } return null; } @Override public void logout(String token) { redisTemplate.delete(token); // 从redis中删除token } }