java和golang使用rsa jwt

在此示例中,使用了Spring Boot和Go gin架构,实现了java项目生成jwt token,go项目验证token。我们把jwt rsa相关信息配置在application.yml中。

Spring Boot中的相关代码和配置

JwtProperties

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("jwt")
public class JwtProperties {
    private String publicKey;

    private String privateKey;

    public String getPublicKey() {
        return publicKey;
    }

    public void setPublicKey(String publicKey) {
        this.publicKey = publicKey;
    }

    public String getPrivateKey() {
        return privateKey;
    }

    public void setPrivateKey(String privateKey) {
        this.privateKey = privateKey;
    }
}

JwtConfiguration

import java.io.IOException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtEncoder;

import com.nimbusds.jose.jwk.JWK;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.ImmutableJWKSet;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;

@Configuration
@EnableConfigurationProperties(JwtProperties.class)
public class JwtConfiguration {
    @Autowired
    private JwtProperties jwtProperties;

    @Bean
    RSAPublicKey rsaPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException {
        String publicKeyPEM = jwtProperties.getPublicKey().replace("-----BEGIN PUBLIC KEY-----", "")
                .replaceAll(System.lineSeparator(), "").replace("-----END PUBLIC KEY-----", "").replaceAll("\\s+", "");

        byte[] encoded = Base64.getDecoder().decode(publicKeyPEM);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(encoded);
        return (RSAPublicKey) keyFactory.generatePublic(keySpec);
    }

    @Bean
    public RSAPrivateKey rsaPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException {
        String privateKeyPEM = jwtProperties.getPrivateKey().replace("-----BEGIN PRIVATE KEY-----", "")
                .replaceAll(System.lineSeparator(), "").replace("-----END PRIVATE KEY-----", "").replaceAll("\\s+", "");

        byte[] encoded = Base64.getDecoder().decode(privateKeyPEM);

        KeyFactory keyFactory = KeyFactory.getInstance("RSA");

        EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(encoded);

        return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
    }

    @Bean
    public JwtDecoder jwtDecoder(RSAPublicKey rsaPublicKey) {
        return NimbusJwtDecoder.withPublicKey(rsaPublicKey).build();
    }

    @Bean
    public JwtEncoder jwtEncoder(RSAPublicKey rsaPublicKey, RSAPrivateKey rsaPrivateKey) {
        JWK jwk = new RSAKey.Builder(rsaPublicKey).privateKey(rsaPrivateKey).build();
        JWKSource jwks = new ImmutableJWKSet<>(new JWKSet(jwk));

        return new NimbusJwtEncoder(jwks);
    }
}

application.yml

jwt:
  public-key: |-
    -----BEGIN PUBLIC KEY-----
    MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3FlqJr5TRskIQIgdE3Dd
    7D9lboWdcTUT8a+fJR7MAvQm7XXNoYkm3v7MQL1NYtDvL2l8CAnc0WdSTINU6IRv
    c5Kqo2Q4csNX9SHOmEfzoROjQqahEcve1jBXluoCXdYuYpx4/1tfRgG6ii4Uhxh6
    iI8qNMJQX+fLfqhbfYfxBQVRPywBkAbIP4x1EAsbC6FSNmkhCxiMNqEgxaIpY8C2
    kJdJ/ZIV+WW4noDdzpKqHcwmB8FsrumlVY/DNVvUSDIipiq9PbP4H99TXN1o746o
    RaNa07rq1hoCgMSSy+85SagCoxlmyE+D+of9SsMY8Ol9t0rdzpobBuhyJ/o5dfvj
    KwIDAQAB
    -----END PUBLIC KEY-----
  private-key: |-
    -----BEGIN PRIVATE KEY-----
    MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDcWWomvlNGyQhA
    iB0TcN3sP2VuhZ1xNRPxr58lHswC9Cbtdc2hiSbe/sxAvU1i0O8vaXwICdzRZ1JM
    g1TohG9zkqqjZDhyw1f1Ic6YR/OhE6NCpqERy97WMFeW6gJd1i5inHj/W19GAbqK
    LhSHGHqIjyo0wlBf58t+qFt9h/EFBVE/LAGQBsg/jHUQCxsLoVI2aSELGIw2oSDF
    oiljwLaQl0n9khX5ZbiegN3OkqodzCYHwWyu6aVVj8M1W9RIMiKmKr09s/gf31Nc
    3WjvjqhFo1rTuurWGgKAxJLL7zlJqAKjGWbIT4P6h/1Kwxjw6X23St3OmhsG6HIn
    +jl1++MrAgMBAAECggEBAMf820wop3pyUOwI3aLcaH7YFx5VZMzvqJdNlvpg1jbE
    E2Sn66b1zPLNfOIxLcBG8x8r9Ody1Bi2Vsqc0/5o3KKfdgHvnxAB3Z3dPh2WCDek
    lCOVClEVoLzziTuuTdGO5/CWJXdWHcVzIjPxmK34eJXioiLaTYqN3XKqKMdpD0ZG
    mtNTGvGf+9fQ4i94t0WqIxpMpGt7NM4RHy3+Onggev0zLiDANC23mWrTsUgect/7
    62TYg8g1bKwLAb9wCBT+BiOuCc2wrArRLOJgUkj/F4/gtrR9ima34SvWUyoUaKA0
    bi4YBX9l8oJwFGHbU9uFGEMnH0T/V0KtIB7qetReywkCgYEA9cFyfBIQrYISV/OA
    +Z0bo3vh2aL0QgKrSXZ924cLt7itQAHNZ2ya+e3JRlTczi5mnWfjPWZ6eJB/8MlH
    Gpn12o/POEkU+XjZZSPe1RWGt5g0S3lWqyx9toCS9ACXcN9tGbaqcFSVI73zVTRA
    8J9grR0fbGn7jaTlTX2tnlOTQ60CgYEA5YjYpEq4L8UUMFkuj+BsS3u0oEBnzuHd
    I9LEHmN+CMPosvabQu5wkJXLuqo2TxRnAznsA8R3pCLkdPGoWMCiWRAsCn979TdY
    QbqO2qvBAD2Q19GtY7lIu6C35/enQWzJUMQE3WW0OvjLzZ0l/9mA2FBRR+3F9A1d
    rBdnmv0c3TcCgYEAi2i+ggVZcqPbtgrLOk5WVGo9F1GqUBvlgNn30WWNTx4zIaEk
    HSxtyaOLTxtq2odV7Kr3LGiKxwPpn/T+Ief+oIp92YcTn+VfJVGw4Z3BezqbR8lA
    Uf/+HF5ZfpMrVXtZD4Igs3I33Duv4sCuqhEvLWTc44pHifVloozNxYfRfU0CgYBN
    HXa7a6cJ1Yp829l62QlJKtx6Ymj95oAnQu5Ez2ROiZMqXRO4nucOjGUP55Orac1a
    FiGm+mC/skFS0MWgW8evaHGDbWU180wheQ35hW6oKAb7myRHtr4q20ouEtQMdQIF
    snV39G1iyqeeAsf7dxWElydXpRi2b68i3BIgzhzebQKBgQCdUQuTsqV9y/JFpu6H
    c5TVvhG/ubfBspI5DhQqIGijnVBzFT//UfIYMSKJo75qqBEyP2EJSmCsunWsAFsM
    TszuiGTkrKcZy9G0wJqPztZZl2F2+bJgnA6nBEV7g5PA4Af+QSmaIhRwqGDAuROR
    47jndeyIaMTNETEmOnms+as17g==
    -----END PRIVATE KEY-----

Go gin中相关代码和配置

token_subject

package model

type TokenSubject struct {
    Id       int64  `json:"id"`
    Name     string `json:"name"`
    UserType int    `json:"userType"`
    Platform string `json:"platform"`
    App      string `json:"app"`
}

jwt_middleware

package middleware

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/gin-gonic/gin"
)

func AuthorizeJWT() gin.HandlerFunc {
    return func(c *gin.Context) {
        const BEARER_SCHEMA = "Bearer"

        authHeader := c.GetHeader("Authorization")

        if len(authHeader) == 0 || len(authHeader) < len(BEARER_SCHEMA)+1 {
            c.AbortWithStatus(http.StatusUnauthorized)

            return
        }

        tokenStr := strings.TrimSpace(authHeader[len(BEARER_SCHEMA)+1:])

        if tokenSubject, err := service.JwtAuthService.ValidAuthToken(tokenStr); err != nil {
            fmt.Println(err)

            c.AbortWithStatus(http.StatusUnauthorized)

            return
        } else if tokenSubject == nil || tokenSubject.Id == 0 {
            c.AbortWithStatus(http.StatusUnauthorized)

            return
        } else {
            c.Set("tokenSubject", tokenSubject)
            c.Next()
        }

    }
}

jwt_auth_service

package service

import (
    "encoding/json"
    "log"

    "github.com/golang-jwt/jwt/v4"
)

type jwtAuthService struct {
}

var JwtAuthService = new(jwtAuthService)

func (s *jwtAuthService) ValidAuthToken(tokenStr string) (*model.TokenSubject, error) {
    if tokenStr == "" {
        return nil, errors.New("参数tokenStr为空")
    }

    claims := &jwt.RegisteredClaims{}

    result, _, err := s.VerifyJWTRSA(tokenStr, config.GetConfig().Jwt.PublicKey, claims)

    if !result {
        return nil, errors.New("验证失败")
    }

    if err != nil {
        log.Println(tokenStr, config.GetConfig().Jwt.PublicKey, err)

        return nil, err
    }

    tokenSubject := &model.TokenSubject{}

    if err := json.Unmarshal([]byte(claims.Subject), &tokenSubject); err != nil {
        log.Println(err)

        return nil, err
    }

    if tokenSubject.Id == 0 {
        return nil, err
    }

    return tokenSubject, nil
}

func (s *jwtAuthService) VerifyJWTRSA(token, publicKey string, claims *jwt.RegisteredClaims) (bool, *jwt.Token, error) {
    var parsedToken *jwt.Token

    // parse token
    state, err := jwt.ParseWithClaims(token, claims, func(token *jwt.Token) (interface{}, error) {

        // ensure signing method is correct
        if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
            return nil, errors.New("unknown signing method")
        }

        parsedToken = token

        // verify
        key, err := jwt.ParseRSAPublicKeyFromPEM([]byte(publicKey))

        if err != nil {
            return nil, err
        }

        return key, nil
    })

    if err != nil {
        return false, &jwt.Token{}, err
    }

    if !state.Valid {
        return false, &jwt.Token{}, errors.New("invalid jwt token")
    }

    return true, parsedToken, nil
}

你可能感兴趣的:(java和golang使用rsa jwt)