一連の記事:
SpringBoot + Vue フロントエンドとバックエンドの分離プロジェクトの実践 || 1 つ: Vue フロントエンド設計
SpringBoot + Vue フロントエンドとフロントエンドの分離プロジェクトの実践 || 2 つ: Spring Boot バックエンドとデータベース接続
SpringBoot + Vue フロントエンドとフロントエンド分離プロジェクトの実践 || 3: Spring Boot バックエンドと Vue フロントエンド接続
SpringBoot + Vue フロントエンドとバックエンド分離プロジェクトの実践 || 4: ユーザー管理機能実装
SpringBoot + Vue フロントエンド分離プロジェクト実践 || 5: ユーザー管理機能フォローアップ
記事ディレクトリ
フロントエンドとバックエンドのドッキング
バックエンドに接続するようにフロントエンド インターフェイスを変更する
src\api\user.js
リクエストアドレスを変更してバックエンドとの一貫性を保つ
src\utils\request.js
フロントエンドのX-Token
フィールドを記録する
開発環境のリクエストアドレスをバックエンドアドレスに変更するhttp://localhost:9999
フロントエンドのモックデータサービスのアノテーションをオフにする
全体的なバックエンド構成
バックエンドで新しいconfig
パッケージを作成し、パッケージ内に 2 つの新しいクラスを作成します
MyCorsConfig
非同期アクセスを構成し、フロントエンド アクセス リンクに接続するために使用され"http://localhost:8888"
ます。この構成はNginx
代わりに使用できます。MyRedisConfig
Redis シリアル化サービスの構成に使用されます
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();
// 允许什么网址来异步访问
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
設定に使用されるコードは次のとおりです。
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;
}
}
バックエンドでログインおよびログアウトのビジネス コードを作成する
フロントエンド VUE プロジェクトのログイン インターフェイスのリクエスト メソッドはPOST
以前紹介したとおりです。
ログイン制御UserController
用のコードを追加する
@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,"用户名或密码错误");
}
下図に示すようにuserService.login()
、メソッドが定義または実装されていないため、メソッドが普及することになります。このとき、マウスをクリックして を押します。Alt+Enter
最初の項目を選択します:
IDEA はインターフェースコードを自動的に生成します
このとき、インターフェースの上にプロンプトが表示され1 related problem
、マウスの左クリックでインターフェースの実装コードにジャンプします。UserServiceImpl
クラス全体の定義でも同様にAlt + Enter
最初のクラスを選択し、ポップアップダイアログボックスで最初のクラスを選択するとコードが生成されます。
生成されたコード
この関数に次のコードを記述します
@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;
}
戻りますUserController
。この時点ではコードは正常です
上記の方法に従い、取得したコードとコード
を記述します。UserController
token
logout
このうち、この 2 つのインターフェイスは実装されていません
コードは以下のように表示されます
@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();
Alt + Enter
それからバグを修正する時が来ました
最初にUserServiceImpl
定義しますredisTemplate
次に、UserServiceImpl
次のコードを記述します
@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
}
赤丸内のコードは結合テーブル クエリです。これはカスタマイズされた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>
テスト
設定されているのでredis
、SpringBootを起動する前にRedisが起動されます。
まずredisのインストールディレクトリを見つけます
cmd
ディレクトリを開いて
コマンドを実行しredis-server.exe redis.windows.conf
、Enter キーを押すと、次のインターフェイスが表示されます。このウィンドウを最小化します。閉じないでください。
次にSprinfBoot
バックエンドを起動します
次にVue
フロントエンドを起動します
クリックしてログインすると、http://localhost:9999/user/login
インターフェースアドレスの変更が確認できます。
バックエンドで生成されたものtoken
も登録されますredis
。
をクリックする退出登录
と、redis 内のトークンもキャンセルされます
バックエンド上のすべてのコード
メモ取りミスを防ぎ、すべてのコードを添付
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 //处理跨域,因为前端和后端的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
コードイン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> * 服务类 * </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> * 服务实现类 * </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 } }