Spring 返回 401 而不是 200 状态
Posted
技术标签:
【中文标题】Spring 返回 401 而不是 200 状态【英文标题】:Spring returns 401 instead of 200 status 【发布时间】:2019-03-23 01:57:48 【问题描述】:作为学习 Spring 的一部分,我编写了一个应用程序,但是当我测试身份验证时,我收到 401 状态而不是 200。我正在寻找错误的原因,在我看来 Authentication authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(email, password));
行返回 null
.但是,我不知道如何解决这个问题。
@Component
public class AuthenticationServiceUsernamePassword
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationServiceUsernamePassword.class);
@Autowired
@Qualifier("customAuthenticationManager")
private AuthenticationManager authenticationManager;
@Autowired
private TokenManager tokenManager;
public SignedJWT authenticate(final String email, final String password)
try
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(email, password));
SecurityContextHolder.getContext()
.setAuthentication(authentication);
if (authentication.getPrincipal() != null)
return tokenManager.createNewToken((PrincipalUser) authentication.getPrincipal());
catch (AuthenticationException authException)
LOGGER.debug("Authentication failed for user:\"" + email + ".\" Reason " + authException.getClass());
return null;
控制器
@Controller
public class AuthController
@Value("$jwt.result")
private String defaultTokenResponse;
@Autowired
private AuthenticationServiceUsernamePassword authUserPassword;
@RequestMapping(value = "/authentication", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> authenticate(String email, String password, HttpServletRequest request,
HttpServletResponse response)
if (email != null && password != null)
try
SignedJWT token = authUserPassword.authenticate(email, password);
if (token != null)
return new ResponseEntity<String>(String.format(defaultTokenResponse, token.serialize()),
HttpStatus.OK);
else
return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
catch (BadCredentialsException badCredentials)
return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
else
return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class ConnectControllerTest
protected MockMvc mockMvc;
@Autowired
private WebApplicationContext context;
@Autowired
private Filter springSecurityFilterChain;
@Before
public void setup()
mockMvc = MockMvcBuilders.webAppContextSetup(context)
.addFilters(springSecurityFilterChain)
.defaultRequest(get("/"))
.build();
@Test
public void shouldTestAuthentication() throws Exception
String result = mockMvc.perform(post("/authentication")
.param("email", "user@test.pl").param("password", "password"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.token").exists())
.andReturn().getResponse().getContentAsString();
如果有人对此处的其余代码感兴趣,请点击链接:repository
【问题讨论】:
你得到一个空指针异常吗?还是您的 catch 子句中的日志消息已写入您的日志? @SvenHakvoort 以上都不是。我刚收到java.lang.AssertionError: Status expected:<200> but was:<401>
。
【参考方案1】:
您已向单元测试用例添加参数。不是有效载荷。如果添加有效负载,则必须使用 @RequestBody。在这里你必须使用@RequestParam。在控制器中使用以下代码:
public ResponseEntity<String> authenticate(@RequestParam String email, @RequestParam String password, HttpServletRequest request,HttpServletResponse response)
..do stuff
这将工作..!
【讨论】:
【参考方案2】:好的。第一要务
Email
和Password
传递正确
问题来了
public SignedJWT authenticate(final String email, final String password)
try
System.out.println("test => "+email+" : "+password);
Authentication authentication = authenticationManager
.authenticate(new UsernamePasswordAuthenticationToken(email, password));
SecurityContextHolder.getContext().setAuthentication(authentication);
if (authentication.getPrincipal() != null)
return tokenManager.createNewToken((PrincipalUser) authentication.getPrincipal());
catch (AuthenticationException authException)
authException.printStackTrace();
LOGGER.debug("Authentication failed for user:\"" + email + ".\" Reason " + authException.getClass());
System.out.println("return nulll");
return null;
如果你运行你的测试用例,它会抛出以下错误
org.springframework.security.authentication.BadCredentialsException: Bad credentials
at org.springframework.security.authentication.dao.DaoAuthenticationProvider.additionalAuthenticationChecks(DaoAuthenticationProvider.java:98)
at org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider.authenticate(AbstractUserDetailsAuthenticationProvider.java:166)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174)
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199)
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:504)
at com.github.springjwt.security.jwt.service.AuthenticationServiceUsernamePassword.authenticate(AuthenticationServiceUsernamePassword.java:30)
at com.github.springjwt.web.api.controller.AuthController.authenticate(AuthController.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImp
这意味着您的测试用例的 username
和 password
与 UserRepository
类用户详细信息不匹配
在您的UserRepository
班级中
您需要设置正确的散列密码及其设置为 null 的盐值。
当您调用authenticate.authenticate
时,它会在内部获取密码和哈希并将其与传递的值匹配。
如果值不匹配,则会引发错误凭据错误
PS:我是在本地运行你的代码后得出这个结论
【讨论】:
如果我将当前的UserRepository
更改为public interface UserRepository extends JpaRepository<User UUID>
会怎样?在这种情况下我还需要设置散列密码吗?
是的,你可以。底线是密码传递,哈希密码应该在使用盐逻辑编码后匹配。否则你会得到 404。
我更改了我的 UserRepository
并使用匹配的数据创建了我的测试用户,但仍然得到 401。我可能做错了什么,但我不知道是什么。我所做的所有更改都在存储库中可见。【参考方案3】:
您的代码大部分是正确的,但在您的控制器定义中出现了错误:
public ResponseEntity<String> authenticate(String email, String password, HttpServletRequest request,
HttpServletResponse response)
默认情况下,Spring 不知道如何检索电子邮件和密码变量。您需要使用 @RequestBody
注释对这些进行注释,例如:
public ResponseEntity<String> authenticate(@RequestBody String email, @RequestBody String password, HttpServletRequest request,
HttpServletResponse response)
但是,如果您的整个控制器将用作 API,您还可以使用 @RestController
注释您的控制器,这告诉 spring 对每个参数使用 @RequestBody
,并且每个方法都应该使用 @ResponseBody
注释,这将告诉 spring应该将返回值转换为 JSON(这对 API 来说很方便)。
参考资料:
Spring’s RequestBody and ResponseBody Annotations
【讨论】:
我用@RestController
注释了我的控制器,但它并没有解决问题。依次使用@RequestBody
注解将状态更改为400
。
使用@RequestParam 而不是@RequestBody。
@VishwPatel,这是不正确的。 @RequestParam
用于 GET 变量,而不用于 POST 参数。以上是关于Spring 返回 401 而不是 200 状态的主要内容,如果未能解决你的问题,请参考以下文章
使用错误凭据的 Django 登录返回 200 而不是 401
为啥 Spring Boot Actuator 返回 404 而不是 401?
对于未经身份验证的请求,Keycloak spring boot 返回 403 而不是 401