我们正在寻找的方案如下:
200 OK
和登录令牌但是,我发现客户端正在获取302
和Location
标头以及令牌。所以它确实进行了身份验证,但使用了不需要的HTTP响应状态代码和标头。
Spring Security配置如下所示:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf()
.disable() // Refactor login form
// See https://jira.springsource.org/browse/SPR-11496
.headers()
.addHeaderWriter(new XFrameOptionsHeaderWriter(XFrameOptionsHeaderWriter.XFrameOptionsMode.SAMEORIGIN))
.and()
.formLogin()
.loginPage("/signin")
.permitAll()
.and()
.logout()
.logoutUrl("/signout")
.permitAll()
.and()
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated();
...
}
我尝试添加拦截器和过滤器,但无法查看在Spring端设置和添加302和位置的位置。
但是,Location
标头确实显示在客户端收到的响应标头中(以及其他Spring Security标头LINK):
Server=Apache-Coyote/1.1
X-Content-Type-Options=nosniff
X-XSS-Protection=1; mode=block
Cache-Control=no-cache, no-store, max-age=0, must-revalidate
Pragma=no-cache
Expires=0
X-Frame-Options=DENY, SAMEORIGIN
Set-Cookie=JSESSIONID=D1C1F1CE1FF4E1B3DDF6FA302D48A905; Path=/; HttpOnly
Location=http://ec2-35-166-130-246.us-west-2.compute.amazonaws.com:8108/ <---- ouch
Content-Length=0
Date=Thu, 22 Dec 2016 20:15:20 GMT
任何建议如何使其按预期工作(&#34; 200 OK&#34;,没有位置标题和令牌)?
注意:使用Spring Boot,Spring Security,无UI,只需调用REST端点的客户端代码。
答案 0 :(得分:1)
如果您需要休息api,则不得使用http.formLogin()
。它按照here所述生成基于表单的登录。
相反,您可以使用此配置
httpSecurity
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.antMatchers("/login").permitAll()
.anyRequest().authenticated()
.and()
.logout()
.disable()
.addFilterBefore(authTokenFilter, UsernamePasswordAuthenticationFilter.class);
创建一个类AuthTokenFilter
,它扩展Spring UsernamePasswordAuthenticationFilter
并覆盖doFilter
方法,该方法检查每个请求中的身份验证令牌并相应地设置SecurityContextHolder
。
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse resp = (HttpServletResponse) response;
resp.setHeader("Access-Control-Allow-Origin", "*");
resp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
resp.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, " + tokenHeader);
HttpServletRequest httpRequest = (HttpServletRequest) request;
String authToken = httpRequest.getHeader(tokenHeader);
String username = this.tokenUtils.getUsernameFromToken(authToken); // Create some token utility class to manage tokens
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(-------------);
// Create an authnetication as above and set SecurityContextHolder
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
然后创建一个AuthenticationController
,用/login
url映射,检查凭据,然后返回令牌。
/*
* Perform the authentication. This will call Spring UserDetailsService's loadUserByUsername implicitly
* BadCredentialsException is thrown if username and password mismatch
*/
Authentication authentication = this.authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(
authenticationRequest.getUsername(),
authenticationRequest.getPassword()
)
);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImp userDetails = (UserDetailsImp) authentication.getPrincipal();
// Generate token using some Token Utils class methods, using this principal
要了解loadUserByUsername
,UserDetailsService
和UserDetails
,请参阅Spring security docs
}
为了更好地理解,请仔细阅读以上链接和后续章节。
答案 1 :(得分:1)
http.formLogin()
专为基于表单的登录而设计。因此,如果您尝试在未经过身份验证的情况下访问受保护资源,则可以预期响应中的302状态和位置标头。
根据您的要求/方案,
- 客户端使用REST连接到REST登录URL
醇>
您是否考虑过使用HTTP Basic进行身份验证?
http.httpBasic()
使用HTTP Basic,您可以使用用户名/密码填充Authorization标头,BasicAuthenticationFilter
将负责验证凭据并相应地填充SecurityContext。
我在客户端使用Angular,在后端使用Spring Boot-Spring Security,有working example。
如果查看security-service.js
,您会看到一个名为securityService
的工厂,它提供login()
功能。此函数根据HTTP Basic格式使用填充了用户名/密码的/principal
标头调用Authorization
端点,例如:
Authorization : Basic base64Encoded(username:passsword)
BasicAuthenticationFilter
将通过提取凭据并最终验证用户并使用经过身份验证的委托人填充SecurityContext
来处理此请求。身份验证成功后,请求将继续到目标端点/principal
,该端点映射到SecurityController.currentPrincipal
,它只返回经过身份验证的主体的json表示。
您的剩余要求:
- Spring微服务(使用Spring Security)应返回200 OK和登录令牌
- 客户端保留令牌
- 客户端使用相同的令牌调用其他REST端点。
醇>
您可以生成安全/登录令牌并返回该令牌而不是用户信息。但是,如果您在不同的微服务中部署了许多需要通过安全令牌保护的REST端点,我强烈建议您查看Spring Security OAuth。构建自己的STS(安全令牌服务)可能变得非常复杂,因此不推荐使用。
答案 2 :(得分:0)
这是一个302响应,告诉浏览器重定向到您的登录页面。你期望发生什么? 302响应必须具有Location标头。
答案 3 :(得分:0)
您可以实现自定义AuthenticationSuccessHandler并覆盖方法“onAuthenticationSuccess”,以根据需要更改响应状态。
示例:
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
ObjectMapper mapper = new ObjectMapper();
Map<String, String> tokenMap = new HashMap<String, String>();
tokenMap.put("token", accessToken.getToken());
tokenMap.put("refreshToken", refreshToken.getToken());
response.setStatus(HttpStatus.OK.value());
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
mapper.writeValue(response.getWriter(), tokenMap);
}
答案 4 :(得分:0)
您需要覆盖默认的注销成功处理程序,以避免重定向。在spring boot2中,您可以执行以下操作:
....logout().logoutSuccessHandler((httpServletRequest,httpServletResponse,authentication)->{
//do nothing not to redirect
})
有关更多详细信息:请检查this。
答案 5 :(得分:-1)
您可以使用headers().defaultsDisabled()
然后将该方法链接以添加所需的特定标题。