欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

vue3整合SpringSecurity加JWT實現(xiàn)登錄認證

 更新時間:2025年04月13日 11:26:09   作者:張喬24  
本文主要介紹了vue3整合SpringSecurity加JWT實現(xiàn)登錄認證,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

前段時間寫了一篇spring security的詳細入門,但是沒有聯(lián)系實際。

所以這次在真實的項目中來演示一下怎樣使用springsecurity來實現(xiàn)我們最常用的登錄校驗。本次演示使用現(xiàn)在市面上最常見的開發(fā)方式,前后端分離開發(fā)。前端使用vue3進行構建,用到了element-plus組件庫、axios封裝、pinia狀態(tài)管理、Router路由跳轉等技術。后端還是spring boot整合springsecurity+JWT來實現(xiàn)登錄校驗。

本文適合有一定基礎的人來看,如果你對springsecurity安全框架還不是很了解,建議你先去看一下我之前寫過的spring security框架的快速入門:

springboot3整合SpringSecurity實現(xiàn)登錄校驗與權限認證(萬字超詳細講解)

技術棧版本:vue3.3.11、springboot3.1.5、spring security6.x

業(yè)務流程:

可以看到整個業(yè)務的流程還是比較簡單的,那么接下來就基于這個業(yè)務流程來進行我們具體代碼的編寫和實現(xiàn);

前端:

新建一個vue項目,并引入一些具體的依賴;我們本次項目用到的有:element-plus、axios、pinia狀態(tài)管理、Router路由跳轉(注意我們在項目中使用到的pinia要引入持久化插件)

vue3中pinia的使用及持久化(詳細解釋)

在vue項目中新建兩個組件:Login.vue(登錄組件,負責登錄頁面的展示)、Layout.vue(布局頁面,負責整體項目的布局,登錄成功之后就是跳轉到這個頁面)

路由的定義:在router文件夾下新建index.ts文件

import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'login',
      component: () => import('@/components/Login.vue')
    },
    {
      path: '/layout',
      name: 'layout',
      component: () => import('@/components/Layout.vue')
    }
  ]
})

export default router

定義Login登錄組件為默認的組件,并定義Layout組件;

useToken的狀態(tài)封裝:在stoers文件夾下新建useToken.ts

import { defineStore } from 'pinia'
import { ref } from 'vue'
const useTokenStore = defineStore('token', ()=>{
const token=ref()


const removeToken=()=>{
    token.value=''
}

return {token,removeToken}
},
{persist: true}
)

export default useTokenStore

axios的封裝:在utils文件夾在新建request.ts文件

import axios from "axios";
import  useTokenStore from '@/stores/useToken'
import { ElMessage } from 'element-plus';
// 先建一個api
const api = axios.create({
    baseURL: "http://localhost:8888",
    timeout: 5000
});
// 發(fā)送請求前攔截
api.interceptors.request.use(
    config =>{
const useToken = useTokenStore();
if(useToken.token){
    console.log("請求頭toekn=====>", useToken.token);
    // 設置請求頭
    // config.headers['token'] = useToken.token;
    config.headers.token = useToken.token;
}
        return config;

},
error =>{

    return Promise.reject(error);
}
)

// 響應前攔截
api.interceptors.response.use(
    response =>{
        console.log("響應數(shù)據(jù)", response);
if(response.data.code !=200){
    ElMessage.error(response.data.message);
}

        return response;
},
error =>{
    return Promise.reject(error);
}
)

export default api;

在請求前攔截,主要是為了在請求頭中新增token。在request.ts中引入了useToken,并判斷如果token不為空,那么在請求頭中新增token。

在響應前也進行了一次攔截,如果后端返回的狀態(tài)碼不為200,那么就打印出錯誤信息;

接下來就可以在Login.vue中進行我們的登錄邏輯的具體編寫了(我直接將組件內容進行復制了,也不是什么太難的東西,主要還是element-plus的表單):

<template>
    <div class="background" style="font-family:kaiti" >

<!-- 注冊表單 -->
<el-dialog v-model="isRegister" title="用戶注冊" width="30%">
    <el-form label-width="120px" v-model="registerForm">
        <el-form-item label="用戶名">
            <el-input type="text"   v-model="registerForm.username"   >
              <template #prefix>
                <el-icon><Avatar /></el-icon>
              </template>
            </el-input>
        </el-form-item>
        <el-form-item label="密碼">
            <el-input  type="password" v-model="registerForm.password" >
              <template #prefix>
        <el-icon><Lock /></el-icon>
        </template>
            </el-input>
        </el-form-item>
        <el-form-item>
            <el-button type="primary" @click="registerAdd" >提交</el-button>
            <el-button @click="isRegister = false">取消</el-button>
        </el-form-item>
    </el-form>
</el-dialog>

<!-- 登陸框 -->
<div class="login-box">
<el-form
    label-width="100px"
    :model="loginFrom"
    style="max-width: 460px"
    :rules="Loginrules"
    ref="ruleFormRef"
  >
    <el-form-item label="用戶名"  prop="username">
      <el-input v-model="loginFrom.username"  clearable  >
        <template #prefix>
                <el-icon><Avatar /></el-icon>
              </template>
        </el-input>
    </el-form-item>
    <el-form-item label="密碼" prop="password">

      <el-input v-model="loginFrom.password"   show-password   clearable  type="password" >
        <template #prefix>
        <el-icon><Lock /></el-icon>
        </template>
      </el-input>
    </el-form-item>

    <el-form-item label="驗證碼"  prop="codeValue">
      <el-input v-model="loginFrom.codeValue"  style="width: 100px;"  clearable  >
      </el-input>
      <img :src="codeImage" @click="getCode" style="transform: scale(0.9);"/>
    </el-form-item>

    <el-button type="success" @click="getLogin(ruleFormRef)"  style="transform: translateX(50px)"  class="my-button">登錄</el-button>
    <el-button type="primary" @click="isRegister=true" class="my-button">注冊</el-button>
  </el-form>

</div>

    </div>
</template>

<script lang="ts" setup>
import { ref,onMounted,reactive } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import useTokenStore  from '@/stores/useToken'
import  api  from '@/utils/request'
import type { FormInstance, FormRules } from 'element-plus'
const ruleFormRef =  ref<FormInstance>()

const loginFrom=ref({
username:'',
password:'',
codeKey:'',
codeValue:''
})

const Loginrules=reactive({

  username: [
    { required: true, message: '請輸入用戶名', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '請輸入密碼', trigger: 'blur' },
    { min: 6, max: 12, message: '長度在 6 到 12 個字符', trigger: 'blur'}
  ],
  codeValue: [
    { required: true, message: '請輸入驗證碼', trigger: 'blur' }
  ]

})

const registerForm=ref({
  username:'',
  password:''
})

const codeImage=ref('')

const isRegister=ref(false)

const tokenStore = useTokenStore();


const router = useRouter()



const getLogin = async(formEl: FormInstance | undefined) => {

  if (!formEl)  return

  await formEl.validate((valid, fields) => {
    if (valid) {
      console.log('submit!')
      
    } else {
      ElMessage('請輸入完整信息')
      return;
    }
  })

  let {data}=await api.post('/user/login',loginFrom.value)

if(data.code==200){
  ElMessage('登錄成功')
  console.log(data);
  tokenStore.token=data.data
  router.replace({name:'layout'})
}else{

  ElMessage('登錄失敗')
}


}


const getCode=async()=>{
  let {data}=await api.get('/getCaptcha')
  loginFrom.value.codeKey=data.data.codeKey
  codeImage.value=data.data.codeValue

}

const registerAdd=async()=>{
let {data}=await api.post('/user/register',registerForm.value)

if(data.code==200){
  ElMessage('注冊成功')
  isRegister.value=false
}else{

  ElMessage('注冊失敗')
  isRegister.value=false
  }

}

// 頁面加載完成獲取驗證碼

onMounted(()=>{
getCode()

})


</script>

這個頁面中,我還加入了一個圖形驗證碼。還有一個注冊的表單。其他的就和普通的登錄一樣了;

這個頁面的最終效果如圖:

Layout.vue頁面中,我們只進行兩個方法的測試;一個是獲取當前用戶的具體信息,一個是退出登錄的按鈕;

<template>
    <div class="common-layout">
    <el-container>
          <el-header height="100px">
            頭部
            <el-button type="primary" @click="getUserInfo">獲取用戶信息</el-button>
            <el-button type="success" @click="Logout">退出登錄</el-button>
        </el-header>
    
    <el-container>
    <el-aside width="200px">
        菜單欄
    </el-aside>
    <el-main>
        展示區(qū)
    </el-main>
    </el-container>
    </el-container>
  </div>


</template>

<script lang="ts" setup name="Layout">
import { ref } from 'vue'
import api from '@/utils/request'
import {ElMessage} from 'element-plus'
import { useRouter } from 'vue-router'
import  useToeknStore from '@/stores/useToken'
const router = useRouter()

const Logout =async () => {
 let data= api.get("/user/logout")
if(data.data.code==200){
ElMessage.success('退出成功')
// 清除token
useToeknStore().removeToken
router.replace({name:'login'})
}
else{
  ElMessage.error('退出失敗')
}

}

const getUserInfo = async() => {

let data=await api.get("/user/info")

console.log('@',data);

}


</script>

數(shù)據(jù)庫:

我新建一個數(shù)據(jù)表,用于登錄校驗:

CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(255) NOT NULL,
    password VARCHAR(255) NOT NULL,
 status INT DEFAULT 0
);

這張表中只有簡單的用戶名,密碼,和用戶是否過期等字段;

后端:

新建一個spring boot項目,并導入以下的依賴:

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>


        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>4.3.0</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.18</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>2.0.21</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

后端使用MybatisPlus做用戶的增、刪、改、查等?;A的controller、service、mapper,我就不再這里進行贅述了;

新建一個類MyTUserDetail ,繼承UserDetail:

@Data
public class MyTUserDetail implements Serializable, UserDetails {

    private static final long serialVersionUID = 1L;

    private Users Users;

    @JsonIgnore  //json忽略
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {

        return null;
    }
    
    @JsonIgnore
    @Override
    public String getPassword() {
        return this.getUsers().getPassword();
    }
    @JsonIgnore
    @Override
    public String getUsername() {
        return this.getUsers().getUsername();
    }

    @JsonIgnore
    @Override
    public boolean isAccountNonExpired() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isAccountNonLocked() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isCredentialsNonExpired() {
        return this.getUsers().getStatus()==0;
    }
    @JsonIgnore
    @Override
    public boolean isEnabled() {
        return this.getUsers().getStatus()==0;
    }
}

新建一個類MyUserDetailServerImpl,實現(xiàn)MyUserDetailServer接口的loadUserByUsername方法

@Service
public class MyUserDetailServerImpl implements MyUserDetailServer {
    @Autowired
    UserMapper userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.selectOne(new LambdaQueryWrapper<User>().
                eq(username != null, User::getUsername, username));
        if (tUser == null) {
            throw new UsernameNotFoundException("用戶名不存在");
        }

        MyTUserDetail myTUserDetail=new MyTUserDetail();
myTUserDetail.setUser(user);
        return myTUserDetail;
    }
}

新建一個JwtUtils的工具類,來生成token;

@Component
public class JwtUtil {

    private final String secret="zhangqiao";

    private final Long expiration=36000000L;

    public String generateToken(Integer id) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + expiration);

        Algorithm algorithm = Algorithm.HMAC256(secret);
        return JWT.create()
                .withSubject(String.valueOf(id))
                .withIssuedAt(now)
                .withExpiresAt(expiryDate)
                .sign(algorithm);
    }

    public Integer getUsernameFromToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return Integer.valueOf(jwt.getSubject());
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /*
    * 判斷token是否過期
    * */
    public boolean isTokenValid(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWT.require(algorithm).build().verify(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /*
    * 刷新token
    * */

    public String refreshToken(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            String username = jwt.getSubject();
            Algorithm algorithm = Algorithm.HMAC256(secret);

            Date now = new Date();
            Date expiryDate = new Date(now.getTime() + expiration);

            return JWT.create()
                    .withSubject(username)
                    .withIssuedAt(now)
                    .withExpiresAt(expiryDate)
                    .sign(algorithm);
        } catch (JWTDecodeException e) {
            return null;
        }
    }
}

新建一個Jwt的攔截類,繼承一個OncePerRequestFilter類,用來在每次請求前攔截請求,并從中獲取token,并判斷這個token是否是我們用戶表中的token;

如果是,那么將用戶信息存儲到security中,這樣后面的過濾器就可以獲取到用戶信息了,如果不是,那么直接放行。我們會將這個攔截器加入到UsernamePasswordAuthenticationFilter過濾器之前。

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //獲取請求頭中的token
        String token = request.getHeader("token");
        System.out.println("前端的token信息=======>"+token);
        //如果token為空直接放行,由于用戶信息沒有存放在SecurityContextHolder.getContext()中所以后面的過濾器依舊認證失敗符合要求
        if(!StringUtils.hasText(token)){
            filterChain.doFilter(request,response);
            return;
        }

//        解析Jwt中的用戶id
        Integer userId = jwtUtil.getUsernameFromToken(token);
        //從redis中獲取用戶信息
        String redisUser = redisTemplate.opsForValue().get(String.valueOf(userId));
        if(!StringUtils.hasText(redisUser)){
            filterChain.doFilter(request,response);
            return;
        }

        MyTUserDetail myTUserDetail= JSON.parseObject(redisUser, MyTUserDetail.class);

        //將用戶信息存放在SecurityContextHolder.getContext(),后面的過濾器就可以獲得用戶信息了。這表明當前這個用戶是登錄過的,后續(xù)的攔截器就不用再攔截了
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken=new UsernamePasswordAuthenticationToken(myTUserDetail,null,null);
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        filterChain.doFilter(request,response);
    }
}

security配置類的設置:

(由于我們本次采用前后端分離的方式來進行開發(fā),所以不在需要使用spring security默認提供的formLogin 方法)

formLogin 方法是 Spring Security 中用于配置基于表單的登錄認證的一種方式。它通常用于傳統(tǒng)的 Web 應用程序,其中前端頁面由后端動態(tài)生成,并且用戶在頁面中輸入用戶名和密碼來進行登錄。在這種情況下,Spring Security 負責處理登錄請求、驗證用戶身份、生成會話等操作。

但是,在前后端分離的開發(fā)模式中,前端和后端是完全分離的,前端負責渲染界面和處理用戶交互,后端負責提供 API 接口和數(shù)據(jù)服務。因此,通常不會使用 formLogin 方法,因為我們的前端不會通過后端渲染的頁面來進行登錄。后端只需要返回一些相應的數(shù)據(jù)和狀態(tài),有關頁面的跳轉和渲染是由前端(vue3)來實現(xiàn)的。

@Configuration
@EnableWebSecurity
public class MyServiceConfig {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;

 


    /*
    * security的過濾器鏈
    * */
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception {
http.csrf(AbstractHttpConfigurer::disable);

http.authorizeHttpRequests((auth) ->
    auth
            .requestMatchers("/getCaptcha","user/login","user/register").permitAll()
            .anyRequest().authenticated()
);
http.cors(cors->{
    cors.configurationSource(corsConfigurationSource());
        });
//自定義過濾器放在UsernamePasswordAuthenticationFilter過濾器之前
    http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);






    return http.build();
}

@Autowired
private MyUserDetailServerImpl myUserDetailsService;



/*
* 驗證管理器
* */
    @Bean
    public AuthenticationManager authenticationManager(PasswordEncoder passwordEncoder){
        DaoAuthenticationProvider provider=new DaoAuthenticationProvider();
//將編寫的UserDetailsService注入進來
        provider.setUserDetailsService(myUserDetailsService);
//將使用的密碼編譯器加入進來
        provider.setPasswordEncoder(passwordEncoder);
//將provider放置到AuthenticationManager 中
        ProviderManager providerManager=new ProviderManager(provider);
        return providerManager;
    }


//跨域配置
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }

    /*
    * 密碼加密器*/
@Bean
    public PasswordEncoder passwordEncoder(){
    return new BCryptPasswordEncoder();
}

}

在security的配置類中,設置了跨域問題、攔截器鏈的配置(并將一些需要放行的接口放行,將我們自定義的Jwt攔截器加入了security攔截鏈)、密碼編譯器、AuthenticationManager 驗證管理等等一系列配置;

Usercontroller控制器:

@RestController
@RequestMapping("/user")
public class UsersController {

    @Autowired
    private IUsersService userService;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    
private  RedisTemplate<String,String> redisTemplate;

@Autowired

private  JwtUtils jwtUtils;

    @PostMapping("/login")
    public Result<String> login(@RequestBody DtoLogin dtoLogin) {
        System.out.println(dtoLogin);
        String token = userService.login(dtoLogin);
        return Result.successData(token);
    }

    @PostMapping("/register")
    public Result register(@RequestBody DtoLogin dtoLogin) {
        System.out.println(dtoLogin);
        Users users = new Users();
        users.setUsername(dtoLogin.getUsername());
        users.setPassword(passwordEncoder.encode(dtoLogin.getPassword()));
        userService.save(users);
        return Result.success();

    }
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private JwtUtil jwtUtil;

  

    @GetMapping("/info")
    public Result info(@RequestHeader("token")String token){
        System.out.println("controller層獲取到的token=======>"+token);
        Integer id = jwtUtil.getUsernameFromToken(token);
        String redisUser = redisTemplate.opsForValue().get(String.valueOf(id));
        MyTUserDetail myTUserDetail = JSON.parseObject(redisUser, MyTUserDetail.class);
        return Result.successData(myTUserDetail);

    }

@GetMapping("user/logout")
 public Result logout(@RequestHeader("token")String token){

//        解析Jwt中的用戶id
        Integer userId = jwtUtil.getUsernameFromToken(token);

//清除SpringSecurity上下文
  SecurityContextHolder.clearContext();

//刪除redis中存儲的用戶數(shù)據(jù)
redisTemplate.delete(Integer.toString(userId));

return Result.success();
}

}

在UserController控制器中,由于登錄方法比較復雜,我將登錄方法重新在service中重寫了,剩下的獲取用戶信息、用戶注冊、退出登錄都直接在UseController中實現(xiàn)了;

service中重寫的登錄方法:

@Service
public class UsersServiceImpl extends ServiceImpl<UsersMapper, Users> implements IUsersService {

    @Autowired
    private RedisTemplate<String,String> redisTemplate;
    @Autowired
    AuthenticationManager authenticationManager;

    @Autowired
    private JwtUtil jwtUtil;


    @Override
    public String login(DtoLogin dtoLogin) {
        String codeRedis = redisTemplate.opsForValue().get(dtoLogin.getCodeKey());
        if (!dtoLogin.getCodeValue().equals(codeRedis)){
            throw new ResultException(400,"驗證碼錯誤");
        }
        // 驗證碼正確,刪除redis中的驗證碼
        redisTemplate.delete(dtoLogin.getCodeKey());

        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(dtoLogin.getUsername(),dtoLogin.getPassword());

        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if(authenticate==null){
            throw new  ResultException(400,"用戶名或密碼錯誤");
        }
//        獲取返回的用戶信息
        Object principal = authenticate.getPrincipal();

        MyTUserDetail myTUserDetail=(MyTUserDetail) principal;
        System.out.println(myTUserDetail);
//        使用Jwt生成token,并將用戶的id傳入
        String token = jwtUtil.generateToken(myTUserDetail.getUsers().getId());
        redisTemplate.opsForValue().
                set(String.valueOf(myTUserDetail.getUsers().getId()), JSON.toJSONString(myTUserDetail),1, TimeUnit.DAYS);


        return token;
    }

}

由于我們還是用了驗證碼,所以在這個登錄方法中先判斷了驗證碼、如果驗證碼正確。那么在判斷傳回來的用戶名和密碼。如果都正確,那么用Jwt返回一個token,token中攜帶的是用戶的id;

至此,我們所有的前后端代碼都已經(jīng)寫完了。那么,讓我們具體的實驗一下;

運行:

由于我剛創(chuàng)建的表,還沒有添加數(shù)據(jù),那么我現(xiàn)在前端點擊注冊,寫入幾條用戶信息;

寫入信息之后,我使用剛注冊過的用戶來登錄一下:

注冊成功之后,就會進入到我們自定義個Layout.vue組件內:

現(xiàn)在,我點擊“獲取用戶信息”按鈕,因為這個路徑我們并沒有放行,那么他訪問時就會被我們自定義的Jwt攔截器攔截,并驗證它請求頭中攜帶的token是否正確。如果正確,則放行。如果不正確,那么就會放行到登錄攔截器中。

可以看到,在控制臺中打印出了用戶的信息。這是肯定的,因為它這次請求攜帶的token是正確的,那么如果我們修改一下token的值,他還能正常訪問到用戶信息這個接口嗎?

我修改了請求頭中的token信息,可以看到立馬這個請求就被攔截了。并爆出了403錯誤;

現(xiàn)在,我點擊“退出登錄”按鈕,它應該刪除useToken中的token值,并且后端也會刪除redis中的值,并且跳轉到登錄頁面。后端也會刪除redsi中存儲的用戶數(shù)據(jù);

現(xiàn)在,我們所有的任務都已經(jīng)完成了。

具體的前后端源碼放在碼云上面了,有需要的可以自行下載:

Vue-Security: 前后端分離的Security

我再整體理一下具體的思路:

前端發(fā)送請求后端,如果是登錄請求,那么直接走登錄接口即可,我將登錄接口進行了方行,任何人都可以訪問到登錄接口,并且執(zhí)行登錄接口的邏輯;如果登錄成功,會返回一個token,前后會將這個token存到useToken中,并且再以后的每次請求中都攜帶token;如果登錄失敗,返回一個報錯信息即可。

如果前端發(fā)送的不是登錄接口,但是前端攜帶可正確的token,那么會被我們自定義的Jwt攔截器攔截,并從中讀取用戶信息,放到security中共后續(xù)的攔截器使用;如果沒有攜帶token,或者token不正確,那么后端會直接返回403的狀態(tài)碼提示;

后續(xù):權限校驗
前后端分離,使用vue3整合SpringSecurity加JWT實現(xiàn)權限校驗-CSDN博客

到此這篇關于vue3整合SpringSecurity加JWT實現(xiàn)登錄認證的文章就介紹到這了,更多相關vue3 SpringSecurity登錄認證內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • vue組件間通信全面講解

    vue組件間通信全面講解

    這篇文章主要介紹了vue組件間通信全面講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-10-10
  • vue中使用require動態(tài)獲取圖片地址方式

    vue中使用require動態(tài)獲取圖片地址方式

    這篇文章主要介紹了vue中使用require動態(tài)獲取圖片地址方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • 手把手教你Vue3實現(xiàn)路由跳轉

    手把手教你Vue3實現(xiàn)路由跳轉

    Vue Router是Vue.js的官方路由器,它與Vue.js核心深度集成,使使用Vue.js構建單頁應用程序變得輕而易舉,下面這篇文章主要給大家介紹了關于Vue3實現(xiàn)路由跳轉的相關資料,需要的朋友可以參考下
    2022-08-08
  • Vue組件中常見的props默認值陷阱問題

    Vue組件中常見的props默認值陷阱問題

    這篇文章主要介紹了避免Vue組件中常見的props默認值陷阱,本文通過問題展示及解決方案給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-09-09
  • vue中的eventBus會不會產(chǎn)生內存泄漏你知道嗎

    vue中的eventBus會不會產(chǎn)生內存泄漏你知道嗎

    這篇文章主要為大家詳細介紹了vue中的eventBus會不會產(chǎn)生內存泄漏,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-02-02
  • vue3中defineEmits與defineProps的用法實例

    vue3中defineEmits與defineProps的用法實例

    這篇文章主要介紹了vue3中defineEmits/defineProps的用法實例,需要的朋友可以參考下
    2023-12-12
  • vue3封裝el-pagination分頁組件的完整代碼

    vue3封裝el-pagination分頁組件的完整代碼

    這篇文章主要介紹了vue3封裝el-pagination分頁組件的完整代碼,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友跟隨小編一起看看吧
    2024-02-02
  • Vue+LogicFlow+Flowable實現(xiàn)工作流

    Vue+LogicFlow+Flowable實現(xiàn)工作流

    本文主要介紹了Vue+LogicFlow+Flowable實現(xiàn)工作流,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-12-12
  • vue.js將unix時間戳轉換為自定義時間格式

    vue.js將unix時間戳轉換為自定義時間格式

    這篇文章主要介紹了vue.js將unix時間戳轉換為自定義時間格式的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-01-01
  • 使用idea創(chuàng)建vue項目的圖文教程

    使用idea創(chuàng)建vue項目的圖文教程

    Vue.js是一套構建用戶界面的框架,只關注視圖層,它不僅易于上手,還便于與第三方庫或既有項目整合,下面這篇文章主要給大家介紹了關于使用idea創(chuàng)建vue項目的相關資料,需要的朋友可以參考下
    2022-08-08

最新評論