Spring boot整合shiro问题400或者302

Spring boot整合shiro问题400或者302

这个问题给我整了一周才解决

记录时间:
时间星期
20248115:30星期日

鹤酒的空间

这里使用的是前后端分离
spring boot
vite vue

疑惑一:为什么400状态了呢?

个人认为首先和重定向没有关系,根前端发送请求格式有关系

接下来看代码,搞了一周才发现不是我shiro的问题是我自己的问题,在研究过程中我发现虽然知道在shiro重定向于重定向之前有关系,但是我依然找不出问题,查阅了很多的帖子/博客/csdn/腾讯云社区等等都没有找到满意的答案,后来我自己又狠下心仔仔细细的检查了一遍,通过对通义千问的依赖和帮助,最后发现了不对的地方,这是我的代码。

后端项目代码

Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.hejiu45</groupId>
	<artifactId>spring-shiro-1</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-shiro-1</name>
	<description>spring-shiro-1</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>3.0.3</version>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter-test</artifactId>
			<version>3.0.3</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.44</version>
		</dependency>


		<!-- shiro -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-spring</artifactId>
			<classifier>jakarta</classifier>
			<version>1.13.0</version>
			<!-- 排除仍使用了javax.servlet的依赖 -->
			<exclusions>
				<exclusion>
					<groupId>org.apache.shiro</groupId>
					<artifactId>shiro-core</artifactId>
				</exclusion>
				<exclusion>
					<groupId>org.apache.shiro</groupId>
					<artifactId>shiro-web</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<!-- 引入适配jakarta的依赖包 -->
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-core</artifactId>
			<classifier>jakarta</classifier>
			<version>1.13.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.shiro</groupId>
			<artifactId>shiro-web</artifactId>
			<classifier>jakarta</classifier>
			<version>1.13.0</version>
			<exclusions>
				<exclusion>
					<groupId>org.apache.shiro</groupId>
					<artifactId>shiro-core</artifactId>
				</exclusion>
			</exclusions>
		</dependency>


<!--		<dependency>-->
<!--			<groupId>javax.servlet</groupId>-->
<!--			<artifactId>javax.servlet-api</artifactId>-->
<!--			<version>4.0.1</version>-->
<!--			<scope>provided</scope>-->
<!--		</dependency>-->
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

ShiroConfig
package com.hejiu45.springshiro1.config;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * shiro 配置类
 */
@Configuration
public class ShiroConfig {

//    shiro filter 工厂
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
        ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
//        CustomShiroFilterFactoryBean factoryBean = new CustomShiroFilterFactoryBean();

//        给拦截器设置安全管理器
        factoryBean.setSecurityManager(securityManager);


        Map<String, String> filterMap = new HashMap<>();
//        系统公共资源
        filterMap.put("/test/scuer", "anon");
        filterMap.put("/test/scuer1", "anon");
        filterMap.put("/test/demo1", "anon");
        filterMap.put("/login", "anon");

        //        系统受限资源
        filterMap.put("/**", "authc"); // authc是受限资源拦截 anon
        filterMap.put("/test/demo", "authc");

//        自定义登录页
        factoryBean.setLoginUrl("/login");

        factoryBean.setFilterChainDefinitionMap(filterMap);

        return factoryBean;
    }
//    安全管理器
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(Realm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

//        给安全管理器一个realm
        securityManager.setRealm(realm);
        return securityManager;
    }

//    自定义realm
    @Bean
    public Realm getRealm(){
        ShiroRealm shiroRealm1 = new ShiroRealm();
        return shiroRealm1;
    }

}

ShiroRealm
package com.hejiu45.springshiro1.config;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class ShiroRealm extends AuthorizingRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        return null;
    }
}

TestController
package com.hejiu45.springshiro1.controller;

import com.hejiu45.springshiro1.entity.Test;
import com.hejiu45.springshiro1.utils.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping("demo")
    public Result demo() {
        log.error("----------------------->demo<-----------------------");
        return new Result(false, 400, "被拦截了");
    }

//    @PostMapping("scuer")
//    public Result scuer(@RequestBody Test test) {
//        log.error("----------------------->scuer<-----------------------");
//        log.error("----------uname---------->[{}]<-----------------------", test.getUname());
//        log.error("----------upasswd---------->[{}]<-----------------------", test.getUpasswd());
//        return new Result(true, 200, "成功进入");
//    }

    @PostMapping("scuer")
    public Result scuer(@RequestParam String uname, @RequestParam String upasswd) {
        log.error("----------------------->scuer<-----------------------");
        log.error("----------uname---------->[{}]<-----------------------", uname);
        log.error("----------upasswd---------->[{}]<-----------------------", upasswd);
        return new Result(true, 200, "成功进入");
    }

    @GetMapping("demo1")
    public Result demo1(@RequestParam String uname, @RequestParam String upasswd) {
        log.error("----------------------->demo<-----------------------");
        log.error("----------uname---------->[{}]<-----------------------", uname);
        log.error("----------upasswd---------->[{}]<-----------------------", upasswd);
        return new Result(false, 400, "被拦截了");
    }

    @PostMapping("scuer1")
    public Result scuer1() {
        log.error("----------------------->scuer<-----------------------");
        return new Result(true, 200, "成功进入");
    }
}

Entity
package com.hejiu45.springshiro1.entity;

import lombok.Data;

/**
* 没有链接数据库
* 只是为了给@RequestBody 提供类
*/
@Data
public class Test {
    private String uname;
    private String upasswd;
}

Utils
package com.hejiu45.springshiro1.utils;

import lombok.Data;
import lombok.NoArgsConstructor;

/**
* Response 数据格式类
*/
@Data
@NoArgsConstructor
public class Result <T> {
    private Boolean flag;
    private Integer code;
    private String mssege;
    private T result;

    public Result(Boolean flag, Integer code, String mssege, T result) {
        this.flag = flag;
        this.code = code;
        this.mssege = mssege;
        this.result = result;
    }

    public Result(Boolean flag, Integer code, String mssege) {
        this.flag = flag;
        this.code = code;
        this.mssege = mssege;
    }
}

前端项目代码

前端项目我使用的是js于vite脚手架于axios和postman做的测试,接下来看前端的代码

Vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [vue()],
  server: {
    port: 8081,
    open: '/',
    // 本地代理
    proxy: {
      // '/api': 'http://localhost:45',
      // 带选项写法:http://localhost:5173/api/bar -> http://jsonplaceholder.typicode.com/bar
      '/api': {
        target: 'http://localhost:45',
        // changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
      // atcors: false
    },
  },
  // 路径别名设置
  resolve: {
    alias: {
      '@': resolve(__dirname, './src'), // 将 @ 符号指向项目的 src 目录
    },
  },
})

Package.json
{
  "name": "vue-spring-shiro",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.7.2",
    "vue": "^3.4.21"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.4",
    "vite": "^5.2.0"
  }
}

Components Vue
<template>
  <div>
    <p>登录页面</p>
    <div>
      账号:
      <input type="text" v-model="uname">
      密码:
      <input type="text" v-model="upasswd">
    </div>
    <div>
      <button @click="getLogin1">登录1</button>
      <button @click="getLogin2">登录2</button>
      <button @click="getLogin3">登录3</button>
      <button @click="getLogin4">登录4</button>
    </div>
  </div>
</template>

<script setup>
import axios from 'axios';
import { ref } from 'vue'

let uname = ref()
let upasswd = ref()

function getLogin1() {
  let url = "http://localhost:8081/api/test/scuer"
  axios.get(url, {
    params: {
      uname: uname.value,
      upasswd: upasswd.value
    }
  })
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })

}

function getLogin2() {
  let url = "http://localhost:8081/api/test/scuer"
  axios.post(url, {
    // params: {
    uname: uname.value,
    upasswd: upasswd.value
    // }
  },
    {
      headers: {
        // 'Content-Type': 'application/json'
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
  )
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })
}

function getLogin3() {
  let url = "http://localhost:8081/api/test/scuer1"
  axios.post(url)
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })
}

function getLogin4() {
  let url = "http://localhost:8081/api/test/demo1"
  axios.get(url, {
    params: {
      uname: uname.value,
      upasswd: upasswd.value
    }
  })
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })
}

</script>

<style scoped>
</style>

现在来说400的问题

我遇到的400问题是,我发现在@PostMapping 与 @RequestParam 与 @RequestBody这几个注解有关系,首先在我测试的4种情况:

请求方式状态码使用注解有无参数参数
GET200@GetMapping无参
GET200@GetMapping与@RequestParam 与 @RequestBody有参数uname/upasswd
POST200@PostMapping无参
POST400@PostMapping与@RequestParam 与 @RequestBody有参数uname/upasswd

这就是测试结果,我也发现为什么post有参会400,就是因为前端发送的请求头中的content-type格式不同,就像我的vue文件中的写的第二个方法一样:

function getLogin2() {
  let url = "http://localhost:8081/api/test/scuer"
  axios.post(url, {
    // params: {
    uname: uname.value,
    upasswd: upasswd.value
    // }
  },
    {
      headers: {
        // 'Content-Type': 'application/json'
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
  )
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })
}
当我带有headers: {

​ // ‘Content-Type’: ‘application/json’
​ ‘Content-Type’: ‘application/x-www-form-urlencoded’
​ }时我的请求就是200,当我不带时就是400.

在然后如果使用的注解是@RequestBody你带还是不带他都200
如果使用的注解是@RequestParam你不带他都400

其次就是不可以在使用注解@RequestParam时前端使用这种方式发送参数:

function getLogin2() {
  let url = "http://localhost:8081/api/test/scuer"
  axios.post(url, {
   params: {
    uname: uname.value,
    upasswd: upasswd.value
   }
  },
    {
      headers: {
        // 'Content-Type': 'application/json'
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    }
  )
    .then(res => {
      console.log(res);
    })
    .catch(err => {
      console.log(err);
    })
}

最终的问题是因为@RequestBody与@RequestParam的不同:AI回答

问题分析

  • 使用@RequestParam

    • 当你在前端使用axios.post()方法发送请求时,如果没有明确指定请求体的内容类型(例如Content-Type: application/x-www-form-urlencoded),默认情况下,axios会将数据序列化为JSON格式。
    • 如果你的后端控制器使用@RequestParam来获取参数,它会在URL查询字符串中查找这些参数。在这种情况下,你发送的JSON数据不会被正确解析,因此会导致400 Bad Request错误。
  • 使用@RequestBody

    • 当你使用@RequestBody注解时,Spring会尝试将请求体中的JSON数据反序列化为Java对象。

    • 如果前端发送的数据格式与后端控制器中的@RequestBody对象匹配,则请求会被正确处理,并且返回200 OK状态码。

疑惑二:为什么302状态了呢?

第一可能是跨域引起的,第二可能是你前端请求地址写错了,第三仔细检查,实在不行就配置后端解决跨域问题

鹤酒前端会持续更新,精彩内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值