我试图通过 JSON 发送用户名和密码,在 spring boot 中使用 flutter 前端制作一个登录页面。 IP 将请求从我的 Android 模拟器发送到本地主机。
按下提交按钮时,将运行以下颤振代码:
Future<String> sendLogin(
String username, String password, BuildContext context) async {
var url = "http://10.0.2.2:8080/login";
var response = await http.post(Uri.parse(url),
headers: <String, String>{"Content-Type": "application/json"},
body: jsonEncode(<String, String>{
"username": username,
"password": password,
}));
url = "http://10.0.2.2:8080/protected-resource";
response = await http.get(Uri.parse(url));
return response.body;
}
我的 Spring Boot 后端看起来像这样
-安全配置:
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private MyAuthenticationProvider authProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
CustomFilter mupaf = new CustomFilter();
mupaf.setAuthenticationManager(authenticationManager());
http
.csrf().disable()
.addFilterAt(
mupaf,
UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/h2/**")
.permitAll()
.and()
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/protected-resource").authenticated()
.antMatchers(HttpMethod.POST, "/login").permitAll()
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"));
http.authenticationProvider(authProvider);
}
}
自定义过滤器:
public class CustomFilter extends AbstractAuthenticationProcessingFilter {
protected CustomFilter() {
super(new AntPathRequestMatcher("/login", "POST"));
}
@Override
public Authentication attemptAuthentication(
HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
String username, password;
try {
Map<String, String> requestMap = new ObjectMapper().readValue(request.getInputStream(), Map.class);
username = requestMap.get("username");
password = requestMap.get("password");
System.out.println(username+ " "+password+" Tried to log in");
} catch (IOException e) {
throw new AuthenticationServiceException(e.getMessage(), e);
}
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
return this.getAuthenticationManager().authenticate(authRequest);
}
}
身份验证提供商:
@Component
@RequiredArgsConstructor
public class MyAuthenticationProvider implements AuthenticationProvider {
private final UserRepository userRepository;
@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
final UsernamePasswordAuthenticationToken upAuth = (UsernamePasswordAuthenticationToken) authentication;
final String name = (String) authentication.getPrincipal();
final String password = (String) upAuth.getCredentials();
final String storedPassword = userRepository.findByUsername(name).map(User::getPassword)
.orElseThrow(() -> new BadCredentialsException("illegal id or passowrd"));
if (Objects.equals(password, "") || !Objects.equals(password, storedPassword)) {
throw new BadCredentialsException("illegal id or passowrd");
}
final Object principal = authentication.getPrincipal();
final UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
principal, authentication.getCredentials(),
Collections.emptyList());
result.setDetails(authentication.getDetails());
return result;
}
@Override
public boolean supports(Class<?> authentication) {
return true;
}
}
当我通过邮递员发送用户名和密码时,我可以登录并访问受保护的资源。但是当我通过flutter前端做同样的事情时,登录成功但是无法访问受保护的资源(错误403)。
我在这里缺少什么导致了这个问题?
发生的情况是,登录后,Spring Boot 使用会话(通常通过 Cookie)来跟踪经过身份验证的状态。当您通过邮递员登录时,它会为您处理会话。但在 flutter 中,您需要自己管理。 因此,您应该使用 http.Client 来保持会话处于活动状态,而不是发出两个单独的请求。具体方法如下: 飞镖
import 'package:http/http.dart' as http;
Future<String> sendLogin(
String username, String password, BuildContext context) async {
var client = http.Client();
var url = "http://10.0.2.2:8080/login";
var response = await client.post(
Uri.parse(url),
headers: <String, String>{"Content-Type": "application/json"},
body: jsonEncode(<String, String>{
"username": username,
"password": password,
}),
);
if (response.statusCode == 200) {
// login was successful, now access protected resource
url = "http://10.0.2.2:8080/protected-resource";
response = await client.get(Uri.parse(url));
return response.body;
} else {
throw Exception('Failed to log in');
}
}
使用 http.Client,会话会转移到第二个请求,因此当您点击 /protected-resource 时,Spring Boot 会识别该会话并允许您访问它。这应该可以解决您遇到的 403 问题。