系列文章:
SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 一:Vue前端設(shè)計(jì)
SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 二:Spring Boot后端與數(shù)據(jù)庫連接
SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 三:Spring Boot后端與Vue前端連接
SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 四:用戶管理功能實(shí)現(xiàn)
SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 五:用戶管理功能后續(xù)
前后端對(duì)接
前端接口修改對(duì)接后端
src\api\user.js
中修改請(qǐng)求地址,與后端保持一致
記錄下前端的src\utils\request.js
中的X-Token
字段
改變開發(fā)環(huán)境中的請(qǐng)求地址,更改為后端地址http://localhost:9999
將前端的模擬數(shù)據(jù)服務(wù)注釋關(guān)閉
后端總體配置
后端新建一個(gè)config
包,包中新建兩個(gè)類
-
MyCorsConfig
用于配置異步訪問,對(duì)接前端的訪問鏈接"http://localhost:8888"
,此配置可用Nginx
代替 -
MyRedisConfig
用于配置Redis序列化服務(wù)
MyCorsConfig
中配置的代碼如下:
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();
// 允許什么網(wǎng)址來異步訪問
configuration.addAllowedOrigin("http://localhost:8888");
// 獲取cookie
configuration.setAllowCredentials(true);
// 允許什么方法? POST、GET,此處為* 意味全部允許
configuration.addAllowedMethod("*");
// 允許所有的請(qǐng)求頭
configuration.addAllowedHeader("*");
// 設(shè)置資源過濾器,過濾什么資源
UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",configuration);
return new CorsFilter(urlBasedCorsConfigurationSource);
}
}
MyRedisConfig
中用于配置的代碼如下:
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);
// 設(shè)置鍵值序列化
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;
}
}
后端編寫登錄登出業(yè)務(wù)代碼
前端VUE項(xiàng)目的登錄接口請(qǐng)求方法為POST
,之前介紹過
在UserController
中新增代碼,用于登錄控制
@PostMapping("/login")
public Result<Map<String,Object>> login(@RequestBody User user){
// 因?yàn)?user傳過來為json字符串,所以用@RequestBody 進(jìn)行實(shí)體轉(zhuǎn)換
// 業(yè)務(wù)代碼在userService里完成
Map<String,Object> data = userService.login(user);
if(data != null){
return Result.success(data,"登錄成功");
}
return Result.fail(2002,"用戶名或密碼錯(cuò)誤");
}
如下圖所示userService.login()
方法會(huì)爆紅,因?yàn)樵摲椒]有被定義或?qū)崿F(xiàn),此時(shí)鼠標(biāo)點(diǎn)擊并按Alt+Enter
選擇第一項(xiàng):
IDEA會(huì)自動(dòng)生成接口代碼
此時(shí),接口上方有個(gè)提示1 related problem
,鼠標(biāo)左擊,會(huì)跳轉(zhuǎn)至接口的實(shí)現(xiàn)代碼處UserServiceImpl
在整個(gè)類的定義之處,同樣Alt + Enter
,選擇第一個(gè),彈出的對(duì)話框再選第一個(gè),會(huì)生成代碼
生成的代碼
在該函數(shù)中寫上下述代碼
@Override
public Map<String, Object> login(User user) {
// 查詢數(shù)據(jù)庫
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(User::getUsername, user.getUsername());
wrapper.eq(User::getPassword, user.getPassword());
User loginUser = this.baseMapper.selectOne(wrapper);
// 結(jié)果不為空,生成token,將用戶信息存入redis
if (loginUser != null) {
// 用UUID,終極方案是jwt
String key = "user:" + UUID.randomUUID();
// 存入redis
loginUser.setPassword(null); // 設(shè)置密碼為空,密碼沒必要放入
redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout為登錄時(shí)間
// 返回?cái)?shù)據(jù)
Map<String, Object> data = new HashMap<>();
data.put("token",key);
return data;
}
// 結(jié)果不為空,生成token,前后端分離,前端無法使用session,可以使用token
// 并將用戶信息存入redis
return null;
}
返回UserController
,此時(shí)代碼正常
按照上述方法如法炮制
在UserController
中寫獲取token
的代碼和logout
代碼
其中,這兩個(gè)接口均未實(shí)現(xiàn)
代碼如下
@GetMapping("/info")
public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){
// @RequestParam("token") 是從url中獲取值
// 根據(jù)token獲取用戶信息,信息存進(jìn)了redis中
Map<String,Object> data = userService.getUserInfo(token);
if(data != null){
return Result.success(data);
}
return Result.fail(2003,"登錄信息無效,請(qǐng)重新登錄");
}
@PostMapping("/logout")
public Result<?> logout(@RequestHeader("X-Token") String token){
userService.logout(token);
return Result.success();
接著就是Alt + Enter
修復(fù)bug
在UserServiceImpl
中先定義一個(gè)redisTemplate
然后在UserServiceImpl
中寫上下述代碼
@Override
public Map<String, Object> getUserInfo(String token) {
// 之前已將對(duì)象進(jìn)行序列化處理存入redis,現(xiàn)在從redis中取出需要反序列化處理
Object obj = redisTemplate.opsForValue().get(token); // 此對(duì)象是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里實(shí)現(xiàn)接口
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
}
注意紅圈中的代碼,是聯(lián)表查詢,這是自定義的SQL
查詢,接下來定義它
找到UserMapper
然后寫上代碼:
public List<String> getRoleNameByUserId(Integer userId);
然后去resources\mapper\sys\UserMapper.xml
中寫SQL語句
<?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>
測(cè)試
由于配置了redis
,所以在啟動(dòng)SpringBoot之前先啟動(dòng)Redis
先找到redis的安裝目錄
打開cmd
定位到該目錄
運(yùn)行命令redis-server.exe redis.windows.conf
,回車,出現(xiàn)下述界面,然后此窗口最小化,千萬別關(guān)閉
接著啟動(dòng)SprinfBoot
后端
然后啟動(dòng)Vue
前端
點(diǎn)擊登錄后,可以看到http://localhost:9999/user/login
接口地址的變化
后端生成的token
也注冊(cè)在redis
中
點(diǎn)擊退出登錄
,redis中的token也被注銷了文章來源:http://www.zghlxwxcb.cn/news/detail-656508.html
后端所有代碼
防止筆記失誤,附上所有代碼文章來源地址http://www.zghlxwxcb.cn/news/detail-656508.html
-
UserController
中的代碼package 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 //處理跨域,因?yàn)榍岸撕秃蠖说腎P一致但端口不一致,所以瀏覽器認(rèn)為跨域,不給訪問,可用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){ // 因?yàn)?user傳過來為json字符串,所以用@RequestBody 進(jìn)行實(shí)體轉(zhuǎn)換 // 業(yè)務(wù)代碼在userService里完成 Map<String,Object> data = userService.login(user); if(data != null){ return Result.success(data,"登錄成功"); } return Result.fail(2002,"用戶名或密碼錯(cuò)誤"); } @GetMapping("/info") public Result<Map<String,Object>> getUserInfo(@RequestParam("token") String token){ // @RequestParam("token") 是從url中獲取值 // 根據(jù)token獲取用戶信息,信息存進(jìn)了redis中 Map<String,Object> data = userService.getUserInfo(token); if(data != null){ return Result.success(data); } return Result.fail(2003,"登錄信息無效,請(qǐng)重新登錄"); } @PostMapping("/logout") public Result<?> logout(@RequestHeader("X-Token") String token){ userService.logout(token); return Result.success(); } }
-
mapper\UserMapper
中的代碼package 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
接口中的代碼package com.ums.sys.service; import com.ums.sys.entity.User; import com.baomidou.mybatisplus.extension.service.IService; import java.util.Map; /** * <p> * 服務(wù)類 * </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
中的代碼package 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> * 服務(wù)實(shí)現(xiàn)類 * </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) { // 查詢數(shù)據(jù)庫 LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>(); wrapper.eq(User::getUsername, user.getUsername()); wrapper.eq(User::getPassword, user.getPassword()); User loginUser = this.baseMapper.selectOne(wrapper); // 結(jié)果不為空,生成token,將用戶信息存入redis if (loginUser != null) { // 用UUID,終極方案是jwt String key = "user:" + UUID.randomUUID(); // 存入redis loginUser.setPassword(null); // 設(shè)置密碼為空,密碼沒必要放入 redisTemplate.opsForValue().set(key, loginUser,30, TimeUnit.MINUTES); // timeout為登錄時(shí)間 // 返回?cái)?shù)據(jù) Map<String, Object> data = new HashMap<>(); data.put("token",key); return data; } // 結(jié)果不為空,生成token,前后端分離,前端無法使用session,可以使用token // 并將用戶信息存入redis return null; } @Override public Map<String, Object> getUserInfo(String token) { // 之前已將對(duì)象進(jìn)行序列化處理存入redis,現(xiàn)在從redis中取出需要反序列化處理 Object obj = redisTemplate.opsForValue().get(token); // 此對(duì)象是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里實(shí)現(xiàn)接口 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 } }
到了這里,關(guān)于SpringBoot + Vue前后端分離項(xiàng)目實(shí)戰(zhàn) || 三:Spring Boot后端與Vue前端連接的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!