【问题标题】:SSO + JWT Validation Best Practice?SSO + JWT 验证最佳实践?
【发布时间】:2020-07-27 13:48:22
【问题描述】:

所以我想问一下有关 JWT 验证的问题。

我的 springboot 项目为除登录和注销端点之外的任何 api 请求实施 jwt 验证。 我的登录和注销端点 (..../api/v1/auth/login, ..../api/v1/auth/logout) 是基于另一个来源的 SSO。 因此,当我使用该 SSO 登录时,他们会将其 JWT 令牌返回到我的后端进程。

然后,当我点击另一个 api 时,我想先用之前生成的 jwt 对其进行验证,但是虽然我的 jwt 匹配,但它总是给出 401 错误。

那么这是我的身份验证类:

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;


import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;


public class AuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    @Value("${xxxx.app.client_secret}")
    private String clientSecret;
    
    private static final Logger logger = LoggerFactory.getLogger(AuthenticationFilter.class);
    
    AuthenticationFilter(final RequestMatcher requiresAuth) {
        super(requiresAuth);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {

        String tokenRequest = httpServletRequest.getHeader("Authorization");
        validateJwtToken(tokenRequest.substring(7));
        Authentication requestAuthentication = new UsernamePasswordAuthenticationToken(tokenRequest, tokenRequest);
        return getAuthenticationManager().authenticate(requestAuthentication);


    }

    
    public boolean validateJwtToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(clientSecret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException e) {
            logger.error("Invalid JWT signature: {}", e.getMessage());
        } catch (MalformedJwtException e) {
            logger.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            logger.error("JWT token is expired: {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
            logger.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            logger.error("JWT claims string is empty: {}", e.getMessage());
        }

        return false;
    }
}

validateJwtToken 方法中,没有问题,因为它给了我 true 返回,但是当它返回尝试身份验证时,它会在该类中捕获这样的错误

catch (InternalAuthenticationServiceException failed) {
            logger.error(
                    "An internal error occurred while trying to authenticate the user.",
                    failed);
            unsuccessfulAuthentication(request, response, failed);

            return;

抱歉我的代码不好,这是我第一次为我的项目实现 jwt 安全性。那么当我遇到这样的情况时该怎么办?谢谢,任何帮助或建议将不胜感激。

【问题讨论】:

    标签: java spring-boot authentication jwt single-sign-on


    【解决方案1】:

    你可以试试这个:如果令牌是由用户名作为主题生成的,那么从令牌中解析它并传递给 UsernamePasswordAuthenticationToken 以在数据库中验证并在 SecurityContext 中设置

    String username = Jwts.parser().setSigningKey(clientSecret).parseClaimsJws(authToken).getBody().getSubject();
    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>()); 
    SecurityContextHolder.getContext().setAuthentication(authentication);
    

    Spring Security UserDetailsS​​ervice 自定义实现例:

    public class UserDeatilsServiceImpl implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       // ApplicationUser - example for entity representing user in application
        ApplicationUser user = // here call your SSO service returing username
        if (user == null) {
            throw new UsernameNotFoundException(username);
        }
        return new User(user.getUsername(), user.getPassword(), emptyList());
    }}
    

    Application SecurityConfiguration 类中的配置:

    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDeatilsServiceImpl userDetailsServiceImpl;
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceImpl);
    }
    // other configurations ...
    

    【讨论】:

    • 不幸的是来自 sso 源的用户名,我没有将其存储到我的数据库中
    • 你可以尝试创建自定义 UserDetailsS​​ervice 实现并在你的 SpringSecurityConfiguration 类中配置它,我已经给出了答案示例
    猜你喜欢
    • 2018-10-06
    • 2020-06-14
    • 2015-10-20
    • 1970-01-01
    • 2011-09-24
    • 1970-01-01
    • 2016-01-09
    • 2014-01-23
    • 2013-01-17
    相关资源
    最近更新 更多