首页 >Java >使用 OAuth 2.0 资源服务器 JWT 时出现权限(授权)问题

使用 OAuth 2.0 资源服务器 JWT 时出现权限(授权)问题

WBOY
WBOY转载
2024-02-13 15:51:091000浏览

在使用 OAuth 2.0 资源服务器 JWT 时,有时会遇到权限(授权)问题。这是许多开发人员在开发过程中经常遇到的一个挑战。OAuth 2.0 是一种用于授权的开放标准,它允许用户授权第三方应用访问其受保护的资源。JWT(JSON Web Token)是一种用于在网络应用之间传递声明的方式。然而,在实践中,可能会出现一些权限问题,比如授权失败、无法获取访问令牌等。本文将针对这些问题进行解析,并提供一些解决方案,帮助开发人员更好地使用 OAuth 2.0 资源服务器 JWT。

问题内容

我正在尝试使用 oauth 2.0 资源服务器 jwt 在一个简单的应用程序中使用 jwt 配置访问授权。

整个身份验证部分工作正常,但我在授权方面遇到问题。即使令牌中存在正确的权限,所有受保护的端点也会给出 403 forbidden 错误。

我尝试使用默认范围(scope_)属性并将配置更改为角色(role_),但问题仍然存在。

有谁知道怎么解决吗?

完整源代码: https://github.com/gustavosc1/spring-security-jwt

生成令牌示例: eyjhbgcioijsuzi1nij9.eyjpc3mioijzchjpbmctc2vjdxjpdhktand0iiwic3viijoidxnlcm5hbwuilcjlehaioje3mdu0ndmyotqsimlhdci6mtcwntqwnzi5ncwicm9szxmi oijst0xfx0fetuloin0.peivwrthx_7mr6eefqbid5dplhffzcvd7scmmt3f7rk7sk1i6kerpqi5ubdvaefnzsjq6vka5nadltsbqidfzogmoixjktfhsc5zrnyyrhikvnwcwb3wrgdd1ezgihaldfjxwxsyypau njwdxznirkl93e6mg1uao5piy9p-9yp8jer7o31wkdr1coskzk3gqw42uecib9h1rrlkx9pdk7pf9rtfssfcwc-ntvismrycreco9rialqfydpdzeojimcbqveyboqfhn2woepgdm8mr5zsdhgdq e1ivsibfbcj_0486zuqiksxp2kniljhl2b5qnan07fjpvslk--ccsg

安全配置:

@configuration
@enablewebsecurity
//@enablemethodsecurity(prepostenabled = true)
public class securityconfig {
  @value("${jwt.public.key}")
  private rsapublickey key;
  @value("${jwt.private.key}")
  private rsaprivatekey priv;

  @bean
  securityfilterchain filterchain(httpsecurity http) throws exception {
    http.csrf(csrf -> csrf.disable())
        .authorizehttprequests(
            auth -> auth
                .requestmatchers("/authenticate").permitall()
                .requestmatchers("/register").permitall()
                .requestmatchers("/private").hasanyrole("admin"))
        .httpbasic(customizer.withdefaults())
        
        // https://docs-spring-io.translate.goog/spring-security/reference/servlet/oauth2/resource-server/jwt.html?_x_tr_sl=en&_x_tr_tl=pt&_x_tr_hl=pt-br&_x_tr_pto=sc
        .oauth2resourceserver(
                conf -> conf.jwt(
                    jwt -> jwt.decoder(jwtdecoder())
                    .jwtauthenticationconverter(jwtauthenticationconverter())));
                
    return http.build();
  }
  
  @bean
  public jwtauthenticationconverter jwtauthenticationconverter() {
      jwtgrantedauthoritiesconverter grantedauthoritiesconverter = new jwtgrantedauthoritiesconverter();
      grantedauthoritiesconverter.setauthoritiesclaimname("roles");
      grantedauthoritiesconverter.setauthorityprefix("role_");

      jwtauthenticationconverter jwtauthenticationconverter = new jwtauthenticationconverter();
      jwtauthenticationconverter.setjwtgrantedauthoritiesconverter(grantedauthoritiesconverter);
      return jwtauthenticationconverter;
  }

  @bean
  jwtdecoder jwtdecoder() {
    return nimbusjwtdecoder.withpublickey(this.key).build();
  }

  @bean
  jwtencoder jwtencoder() {
    jwk jwk = new rsakey.builder(this.key).privatekey(this.priv).build();
    jwksource<securitycontext> jwks = new immutablejwkset<>(new jwkset(jwk));
    return new nimbusjwtencoder(jwks);
  }
  
  @bean
  passwordencoder passwordencoder() {
    return new bcryptpasswordencoder();
  }
  
}

userdetailss​​erviceimpl:

@service
public class userdetailsserviceimpl implements userdetailsservice {
  private final userrepository userrepository;

  public userdetailsserviceimpl(userrepository userrepository) {
    this.userrepository = userrepository;
  }

  @override
  public userdetails loaduserbyusername(string username) throws usernamenotfoundexception {
    optional<user> useroptional = userrepository.findbyusername(username);
    
    user user = useroptional.orelsethrow(() -> new usernamenotfoundexception("user not found with username: " + username));
    
    return new userauthenticated(user.getusername(), user.getpassword());
  }

}

用户已验证:

public class userauthenticated implements userdetails {
  
  private string username;
  private string password;

  public userauthenticated(string username, string password) {
    this.username = username;
    this.password = password;
  }

  @override
  public string getusername() {
    return username;
  }

  @override
  public string getpassword() {
    return password;
  }

  @override
  public collection<? extends grantedauthority> getauthorities() {
    return list.of(() -> "role_admin");
  }

  @override
  public boolean isaccountnonexpired() {
    return true;
  }

  @override
  public boolean isaccountnonlocked() {
    return true;
  }

  @override
  public boolean iscredentialsnonexpired() {
    return true;
  }

  @override
  public boolean isenabled() {
    return true;
  }

}

jwtservice:

@service
public class jwtservice {
  private final jwtencoder encoder;

  public jwtservice(jwtencoder encoder) {
    this.encoder = encoder;
  }

  public string generatetoken(authentication authentication) {
    instant now = instant.now();
    long expiry = 36000l;

    string scope = authentication
        .getauthorities().stream()
        .map(grantedauthority::getauthority)
        .collect(collectors
            .joining(" "));

    jwtclaimsset claims = jwtclaimsset.builder()
        .issuer("spring-security-jwt")
        .issuedat(now)
        .expiresat(now.plusseconds(expiry))
        .subject(authentication.getname())
        .claim("roles", scope)
        .build();

    return encoder.encode(
        jwtencoderparameters.from(claims))
        .gettokenvalue();
  }

}

私人控制器:

@RestController
@RequestMapping("private")
public class PrivateController {

  @GetMapping
  //@PreAuthorize("hasAuthority('ROLE_ADMIN')")
  public String getMessage() {
    return "Hello from private API controller";
  }
  
}

解决方法

已生成提供的令牌,并将 roles 字段设置为 ROLE_ADMIN。在 jwtAuthenticationConverter() 中,您尝试将 setAuthorityPrefixROLE 一起使用,导致 ROLE_ROLE_ADMIN

要纠正此问题,请将该行修改为 grantedAuthoritiesConverter.setAuthorityPrefix("");

进行此调整后,问题应该得到解决。如果您遇到任何其他问题,请告诉我。

注意:

如果省略此步骤,默认前缀将为 SCOPE,导致您的角色变为 SCOPE_ROLE_ADMIN

以上是使用 OAuth 2.0 资源服务器 JWT 时出现权限(授权)问题的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文转载于:stackoverflow.com。如有侵权,请联系admin@php.cn删除