我们有一个现有的应用程序使用spring security rest作为安全机制,我们决定切换到Auth0。 我正在尝试使用Auth0令牌制作grails spring安全休息插件。 根据文档,似乎最简单的实现方法是实现自定义令牌存储,它将根据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
(不知道为什么,但让& #39;假设一会儿它是正确的)在这里:
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));
}
}
我很确定这是我做错的事情,在这一点上可以想到任何事情。 我在这里缺少什么?