tokenAuthenticationService

tokenAuthenticationService

我正在使用最新版本的Spring Boot,并且正在尝试设置StatelessAuthenticaion。到目前为止,我一直在阅读的教程非常模糊,我不确定自己在做什么错。我正在使用的教程是...

http://technicalrex.com/2015/02/20/stateless-authentication-with-spring-security-and-jwt/

我的安装程序存在的问题是,除了从未调用TokenAuthenticationService::addAuthentication之外,似乎一切都正常运行,因此从未设置我的 token ,因此在调用TokenAuthenticationService::getAuthentication时它返回null,因此即使我成功登录也返回401(因为从不调用addAuthentication来设置标头中的 token )。我正在尝试找到一种添加TokenAuthenticationService::addAuthentication的方法,但我发现这很困难。

在本教程中,他在WebSecurityConfig::UserDetailsService.userService中添加了类似于auth.userDetailsService()的内容。我遇到的唯一问题是,当我这样做时,它引发了CastingErrorException。仅当我使用UserDetailsService customUserDetailsService时才有效...

Web安全配置

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的正确解释

token 认证服务
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;
    }
}

token 处理器
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);

哪个调用以下类作为过滤器。它获得了身份验证,但是没有将它实际添加到何处。

StatelessAuthenticationFilter
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中获取它,则可以设置 token 。
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);
    }
}

最佳答案

用户首次提供其登录凭据时,必须自己调用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,它可以解决用户提供的初始登录与所有后续请求中发生的无状态身份验证之间的混淆。

10-06 13:08