我正在使用最新版本的Spring Boot,我正在尝试设置StatelessAuthenticaion。到目前为止,我一直在阅读的教程非常模糊,我不确定我做错了什么。我正在使用的教程是......
http://technicalrex.com/2015/02/20/stateless-authentication-with-spring-security-and-jwt/
我的设置问题是,似乎一切都正常运行,除了TokenAuthenticationService::addAuthentication
从未被调用的事实,因此我的标记永远不会被设置,因此当调用TokenAuthenticationService::getAuthentication
时它返回null因此,即使我成功登录也会返回401(因为从不调用addAuthentication来设置标头中的标记)。我想找到一种方法来添加TokenAuthenticationService::addAuthentication
,但我发现它很难。
在教程中,他向WebSecurityConfig::UserDetailsService.userService
添加了与auth.userDetailsService()
类似的内容。我遇到的唯一问题是,当我这样做时,它会抛出CastingErrorException。它只适用于我使用UserDetailsService customUserDetailsService
而不是......
package app.config;
import app.repo.User.CustomUserDetailsService;
import app.security.RESTAuthenticationEntryPoint;
import app.security.RESTAuthenticationFailureHandler;
import app.security.RESTAuthenticationSuccessHandler;
import app.security.TokenAuthenticationService;
import app.security.filters.StatelessAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.sql.DataSource;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private static PasswordEncoder encoder;
private final TokenAuthenticationService tokenAuthenticationService;
private final CustomUserDetailsService userService;
@Autowired
private UserDetailsService customUserDetailsService;
@Autowired
private RESTAuthenticationEntryPoint authenticationEntryPoint;
@Autowired
private RESTAuthenticationFailureHandler authenticationFailureHandler;
@Autowired
private RESTAuthenticationSuccessHandler authenticationSuccessHandler;
public WebSecurityConfig() {
this.userService = new CustomUserDetailsService();
tokenAuthenticationService = new TokenAuthenticationService("tooManySecrets", userService);
}
@Autowired
public void configureAuth(AuthenticationManagerBuilder auth,DataSource dataSource) throws Exception {
auth.jdbcAuthentication().dataSource(dataSource);
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
http.csrf().disable();
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
http.formLogin().defaultSuccessUrl("/").successHandler(authenticationSuccessHandler);
http.formLogin().failureHandler(authenticationFailureHandler);
//This is ho
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
@Bean
@Override
public CustomUserDetailsService userDetailsService() {
return userService;
}
@Bean
public TokenAuthenticationService tokenAuthenticationService() {
return tokenAuthenticationService;
}
}
TokenAuthenticationService
成功调用了getAuthentication
方法,但在我读过的教程中,没有关于如何调用addAuthentication
的正确解释
package app.security;
import app.repo.User.CustomUserDetailsService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenAuthenticationService {
private static final String AUTH_HEADER_NAME = "X-AUTH-TOKEN";
private final TokenHandler tokenHandler;
//This is called in my WebSecurityConfig() constructor
public TokenAuthenticationService(String secret, CustomUserDetailsService userService) {
tokenHandler = new TokenHandler(secret, userService);
}
public void addAuthentication(HttpServletResponse response, UserAuthentication authentication) {
final UserDetails user = authentication.getDetails();
response.addHeader(AUTH_HEADER_NAME, tokenHandler.createTokenForUser(user));
}
public Authentication getAuthentication(HttpServletRequest request) {
final String token = request.getHeader(AUTH_HEADER_NAME);
if (token != null) {
final UserDetails user = tokenHandler.parseUserFromToken(token);
if (user != null) {
return new UserAuthentication(user);
}
}
return null;
}
}
package app.security;
import app.repo.User.CustomUserDetailsService;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
public final class TokenHandler {
private final String secret;
private final CustomUserDetailsService userService;
public TokenHandler(String secret, CustomUserDetailsService userService) {
this.secret = secret;
this.userService = userService;
}
public UserDetails parseUserFromToken(String token) {
String username = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody()
.getSubject();
return userService.loadUserByUsername(username);
}
public String createTokenForUser(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.signWith(SignatureAlgorithm.HS512, secret)
.compact();
}
}
在我的WebServiceConfig中。我添加以下
http.addFilterBefore(new StatelessAuthenticationFilter(tokenAuthenticationService),
UsernamePasswordAuthenticationFilter.class);
将以下类作为过滤器调用。它获得了身份验证,但是没有它实际添加它的地方。
package app.security.filters;
import app.security.TokenAuthenticationService;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.GenericFilterBean;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* Created by anthonygordon on 11/17/15.
*/
public class StatelessAuthenticationFilter extends GenericFilterBean {
private final TokenAuthenticationService authenticationService;
public StatelessAuthenticationFilter(TokenAuthenticationService authenticationService) {
this.authenticationService = authenticationService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Authentication authentication = authenticationService.getAuthentication(httpRequest);
SecurityContextHolder.getContext().setAuthentication(authentication);
filterChain.doFilter(request, response);
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
SecurityContextHolder.getContext().setAuthentication(null);
}
}
以下类是TokenAuthenticationService::addAuthentication
package app.security;
import app.repo.User.User;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
public class UserAuthentication implements Authentication {
private final UserDetails user;
private boolean authenticated = true;
public UserAuthentication(UserDetails user) {
this.user = user;
}
@Override
public String getName() {
return user.getUsername();
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
}
@Override
public Object getCredentials() {
return user.getPassword();
}
@Override
public UserDetails getDetails() {
return user;
}
@Override
public Object getPrincipal() {
return user.getUsername();
}
@Override
public boolean isAuthenticated() {
return authenticated;
}
@Override
public void setAuthenticated(boolean authenticated) {
this.authenticated = authenticated;
}
}
多数民众赞成......
我的解决方案是在我的成功处理程序中设置TokenAuthenticationService::addAuthentication
方法...唯一的问题是教程将类TokenAuthenticationService
添加到WebServiceConfig类。这是唯一可以访问的地方。如果有一种方法可以在successHandler中获取它,我可以设置令牌。
package app.security;
import app.controllers.Requests.TriviaResponse;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by anthonygordon on 11/12/15.
*/
@Component
public class RESTAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter();
TriviaResponse tresponse = new TriviaResponse();
tresponse.setMessage("You have successfully logged in");
String json = ow.writeValueAsString(tresponse);
response.getWriter().write(json);
clearAuthenticationAttributes(request);
}
}
答案 0 :(得分:2)
当用户第一次提供登录凭据时,您必须自己致电TokenAuthenticationService.addAuthentication()
。
在用户使用Google帐户成功登录后,教程会在addAuthentication()
中调用GoogleAuthorizationResponseServlet
。这是相关代码:
private String establishUserAndLogin(HttpServletResponse response, String email) {
// Find user, create if necessary
User user;
try {
user = userService.loadUserByUsername(email);
} catch (UsernameNotFoundException e) {
user = new User(email, UUID.randomUUID().toString(), Sets.<GrantedAuthority>newHashSet());
userService.addUser(user);
}
// Login that user
UserAuthentication authentication = new UserAuthentication(user);
return tokenAuthenticationService.addAuthentication(response, authentication);
}
如果您已经拥有身份验证成功处理程序,那么我认为您需要从那里调用TokenAuthenticationService.addAuthentication()
。将tokenAuthenticationService
bean注入您的处理程序,然后开始使用它。如果您的成功处理程序最终不是Spring bean,那么您可以通过调用tokenAuthenticationService
明确查看WebApplicationContextUtils.getRequiredWebApplicationContext.getBean(TokenAuthenticationService.class)
。
教程的GitHub仓库中还有一个issue,它将解决用户提供的初始登录与所有后续请求中发生的无状态身份验证之间的混淆。
答案 1 :(得分:1)
你可以定义一个StatelessLoginFilter,如下所示
.addFilterBefore(
new StatelessLoginFilter("/api/signin",
tokenAuthenticationService, userDetailsService,
authenticationManager()),
UsernamePasswordAuthenticationFilter.class)
并像这样编写类
class StatelessLoginFilter extends AbstractAuthenticationProcessingFilter {
private final TokenAuthenticationService tokenAuthenticationService;
private final UserDetailsService userDetailsService;
protected StatelessLoginFilter(String urlMapping,
TokenAuthenticationService tokenAuthenticationService,
UserDetailsService userDetailsService,
AuthenticationManager authManager) {
super(new AntPathRequestMatcher(urlMapping));
this.userDetailsService = userDetailsService;
this.tokenAuthenticationService = tokenAuthenticationService;
setAuthenticationManager(authManager);
}
@Override
protected void successfulAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
Authentication authentication) throws IOException, ServletException {
final User authenticatedUser = userDetailsService
.loadUserByUsername(authentication.getName());
final UserAuthentication userAuthentication = new UserAuthentication(
authenticatedUser);
tokenAuthenticationService.addAuthentication(response,
userAuthentication);
SecurityContextHolder.getContext()
.setAuthentication(userAuthentication);
}
}