1

我们有一个使用 spring security rest 作为安全机制的现有应用程序,我们决定切换到 Auth0。我正在尝试使 grails spring security rest 插件与 Auth0 令牌一起工作。根据文档,似乎实现它的最简单方法是实现自定义令牌存储,它将针对 Auth0 验证令牌

现在,我只是创建了自己的 Token 存储类来开始了解它是如何工作的。我的安全存储实现目前与原来的完全一样JwtTokenStorage(当我试图理解机制时):

package priz.be

import com.nimbusds.jose.JOSEException
import com.nimbusds.jwt.JWT
import grails.plugin.springsecurity.rest.JwtService
import grails.plugin.springsecurity.rest.token.storage.TokenNotFoundException
import grails.plugin.springsecurity.rest.token.storage.TokenStorageService
import grails.plugin.springsecurity.rest.token.storage.jwt.JwtTokenStorageService
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.springframework.security.core.authority.SimpleGrantedAuthority
import org.springframework.security.core.userdetails.User
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UserDetailsService

import java.text.ParseException

@Slf4j
@CompileStatic
class Auth0TokenStorageService extends JwtTokenStorageService {


    JwtService jwtService
    UserDetailsService userDetailsService

    @Override
    UserDetails loadUserByToken(String tokenValue) throws TokenNotFoundException {
        Date now = new Date()
        try {
            JWT jwt = jwtService.parse(tokenValue)

            if (jwt.JWTClaimsSet.expirationTime?.before(now)) {
                throw new TokenNotFoundException("Token ${tokenValue} has expired")
            }
            boolean isRefreshToken = jwt.JWTClaimsSet.expirationTime == null

            if (isRefreshToken) {
                UserDetails principal = userDetailsService.loadUserByUsername(jwt.JWTClaimsSet.subject)

                if (!principal) {
                    throw new TokenNotFoundException("Token no longer valid, principal not found")
                }
                if (!principal.enabled) {
                    throw new TokenNotFoundException("Token no longer valid, account disabled")
                }
                if (!principal.accountNonExpired) {
                    throw new TokenNotFoundException("Token no longer valid, account expired")
                }
                if (!principal.accountNonLocked) {
                    throw new TokenNotFoundException("Token no longer valid, account locked")
                }
                if (!principal.credentialsNonExpired) {
                    throw new TokenNotFoundException("Token no longer valid, credentials expired")
                }

                return principal
            }

            def roles = jwt.JWTClaimsSet.getStringArrayClaim('roles')?.collect { String role -> new SimpleGrantedAuthority(role) }

            log.debug "Successfully verified JWT"

            log.debug "Trying to deserialize the principal object"
            try {
                UserDetails details = JwtService.deserialize(jwt.JWTClaimsSet.getStringClaim('principal'))
                log.debug "UserDetails deserialized: ${details}"
                if (details) {
                    return details
                }
            } catch (exception) {
                log.debug(exception.message)
            }

            log.debug "Returning a org.springframework.security.core.userdetails.User instance"
            return new User(jwt.JWTClaimsSet.subject, 'N/A', roles)
        } catch (ParseException pe) {
            throw new TokenNotFoundException("Token ${tokenValue} is not valid")
        } catch (JOSEException je) {
            throw new TokenNotFoundException("Token ${tokenValue} has an invalid signature")
        }
    }

    @Override
    void storeToken(String tokenValue, UserDetails principal) {
        log.debug "Nothing to store as this is a stateless implementation"
    }

    @Override
    void removeToken(String tokenValue) throws TokenNotFoundException {
        log.debug "Nothing to remove as this is a stateless implementation"
        throw new TokenNotFoundException("Token ${tokenValue} cannot be removed as this is a stateless implementation")
    }
}

如果我使用有效的 Auth0 令牌向 API 发送请求,则会收到无效令牌错误作为响应。

再深入一点,我发现当代码试图验证令牌时,它会进入JwtService.parse函数,在那里它将令牌解析为HMAC signed(不知道为什么,但让我们暂时假设它是正确的)在这里:

JWT parse(String tokenValue) {
        JWT jwt = JWTParser.parse(tokenValue)

        if (jwt instanceof  SignedJWT) {
            log.debug "Parsed an HMAC signed JWT"

            SignedJWT signedJwt = jwt as SignedJWT
            if(!signedJwt.verify(new MACVerifier(jwtSecret))) {
                throw new JOSEException('Invalid signature')
            }
        } else if (jwt instanceof EncryptedJWT) {
          ...

        return jwt
    }

然后它尝试将验证委托给MACVerifier,但是,当它解析算法名称时,它会失败,因为不支持 RS256:

protected static String getJCAAlgorithmName(final JWSAlgorithm alg)
        throws JOSEException {

        if (alg.equals(JWSAlgorithm.HS256)) {
            return "HMACSHA256";
        } else if (alg.equals(JWSAlgorithm.HS384)) {
            return "HMACSHA384";
        } else if (alg.equals(JWSAlgorithm.HS512)) {
            return "HMACSHA512";
        } else {
            throw new JOSEException(AlgorithmSupportMessage.unsupportedJWSAlgorithm(
                alg,
                SUPPORTED_ALGORITHMS));
        }
    }

我很确定这是我做错的事情,此时我可以想到任何事情。我在这里想念什么?

4

0 回答 0