当前位置: 首页 > 知识库问答 >
问题:

当由其他自定义授权服务器(SpringBoot2&OAuth2)进行身份验证时,OAuth2客户端主体没有GrantedAuthorities

明松
2023-03-14

我使用Spring Boot2作为框架,Tymeleaf作为模板引擎。

在我的授权服务器中,我将用户'admin'添加为'role_admin'。

但在客户端应用程序中,当我以'admin'登录并从SecurityContextHolder.GetContext().GetAuthentication()打印Authentication对象时,Granted Authoritions属性只有'role_user'。

下面是我的授权服务器配置。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN");
        auth
                .inMemoryAuthentication()
                .withUser("user").password(passwordEncoder().encode("123")).roles("USER");

    }

下面是SecurityContextHolder.getContext().getAuthentication()的日志记录代码中的Authentication对象。

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.isAuthenticated());
        System.out.println(auth.getAuthorities());
        System.out.println(auth.getPrincipal());

结果是

//  isAuthenticated()
true

// getAuthorites()
[ROLE_USER] 

// getPrincipal()
Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=ROLE_ADMIN}, {authority=ROLE_USER}], ...
            <div sec:authorize="isAuthenticated()">
                Text visible only to authenticated users.

                <!-- Principal name -->
                Authenticated username:
                <div sec:authentication="name"></div>

                <div sec:authorize="hasRole('USER')">Text visible to user.</div>
                <!-- i cant see this message -->
                <div sec:authorize="hasRole('ADMIN')">Text visible to admin.</div>

                Authenticated user roles:
                <!-- print '[ROLE_USER]' only -->
                <div sec:authentication="principal.authorities"></div>
            </div>

            <div sec:authorize="!isAuthenticated()">Text visible only to
                unauthenticated users.
            </div>

因此,我想访问Thymeleaf中的principal.userattributes.authorities

我正在引用Oauth2AuthenticationTokenOauth2User.GetAttributes()DefaultoAuth2User.ToString()

我怎么能这么做?

共有1个答案

井学
2023-03-14

我解决了。

在授权服务器中,我是这样配置的。

  • AuthorizationServerWebSecurityConfigurerAdapterConfig
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    ...
        @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN").authorities("USER", "ADMIN");
        auth
                .inMemoryAuthentication()
                .withUser("user").password(passwordEncoder().encode("123")).roles("USER");

    }
    ...
}

下面是我的资源服务器的/me映射控制器

  • ResourceServer/ME映射控制器
@RestController
public class UserController {

    @RequestMapping("/me")
    public Principal user(Principal principal) {
        return principal;
    }
}

下面是我的客户端的WebSecurityConfigurerAdapter配置

    null
@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
                .anyRequest().authenticated()
                .and().oauth2Login();
    }

在客户端的控制器中,我是这样记录的。

  • 在客户端控制器中记录主体
    @GetMapping("")
    public String git1() {
        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        System.out.println(auth.getPrincipal());

        /** Thymeleaf using this **/
        Object authenticationProperty = AuthUtils.getAuthenticationProperty(auth, "principal.attributes['authorities']");
        System.out.println(authenticationProperty.toString());

        return VIEW_PATH + "git1";
    }

下面是结果

Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=USER}, {authority=ADMIN}], details={remoteAddress=127.0.0.1, sessionId=null, tokenValue=82a7a532-a31e-4d0a-bd83-f15a9cbea3bc, tokenType=Bearer, decodedDetails=null}, authenticated=true, userAuthentication={authorities=[{authority=USER}, {authority=ADMIN}], details=null, authenticated=true, principal=admin, credentials=N/A, name=admin}, oauth2Request={clientId=foo, scope=[read], requestParameters={client_id=foo}, resourceIds=[], authorities=[], approved=true, refresh=false, redirectUri=null, responseTypes=[], extensions={}, refreshTokenRequest=null, grantType=null}, clientOnly=false, principal=admin, credentials=, name=admin]
[{authority=USER}, {authority=ADMIN}]

在资源服务器的principal对象中同时授予了'role_admin'和'role_user'。

但在客户端的principal对象中没有授予'role_admin'。只有“role_user”。

principal.atttiButes['authorities']具有“user”、“admin”。

正如@Rahil Husain所说,存在defaultoauth2userservice,并且该服务只向oauth2user对象授予'role_user'。

首先,我通过@componenet注释添加了CustomAuthoritiesExtractor(@bean)敬客户。

但这在我的项目中不起作用。

因此,我实现了customoauth2usercustomoauth2userservice

就像这样。

    null
public class CustomOAuth2User implements OAuth2User {
    private List<GrantedAuthority> authorities;
    private Map<String, Object> attributes;
    private String name;


    public CustomOAuth2User(List<GrantedAuthority> authorities, Map<String, Object> attributes) {
        this.authorities = authorities;
        this.attributes = attributes;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public Map<String, Object> getAttributes() {
        if (this.attributes == null) {
            this.attributes = new HashMap<>();
            this.attributes.put("name", this.getName());
        }
        return attributes;
    }

    @Override
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
  • CustomoAuth2UserService
public class CustomOAuth2UserService extends DefaultOAuth2UserService {

    @Override
    public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
        OAuth2User oAuth2User = super.loadUser(userRequest);

        AuthoritiesExtractor authoritiesExtractor = new CustomAuthoritiesExtractor();
        List<GrantedAuthority> grantedAuthorityList = authoritiesExtractor.extractAuthorities(oAuth2User.getAttributes());
        CustomOAuth2User customOAuth2User = new CustomOAuth2User(grantedAuthorityList, oAuth2User.getAttributes());
        customOAuth2User.setName(oAuth2User.getName());

        return customOAuth2User;
    }
}

下面是我的CustomAuthoritiesExtractor。这个类不用作@bean@component。直接在CustomoAuth2Service中用于映射CustomoAuth2User对象的权限

  • CustomAuthoritiesExtractor
public class CustomAuthoritiesExtractor implements AuthoritiesExtractor {

    @Override
    public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
        return AuthorityUtils.commaSeparatedStringToAuthorityList(asAuthorities(map));
    }

    private String asAuthorities(Map<String, Object> map) {
        List<String> authorities = new ArrayList<>();
        List<LinkedHashMap<String, String>> authz =
                (List<LinkedHashMap<String, String>>) map.get("authorities");
        for (LinkedHashMap<String, String> entry : authz) {
            authorities.add(entry.get("authority"));
        }
        return String.join(",", authorities);
    }
}

最后,我将客户端的endpoint更改为使用我的CustomoAuth2UserCustomoAuth2UserService

因此,我像这样更改了客户机的WebSecurityConfigurerAdapter配置。

@Configuration
@EnableOAuth2Client
public class WebSecurityConfigurerAdapterImpl extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll()
                .anyRequest().authenticated()
                .and().oauth2Login()


                /** add this config**/
                            .userInfoEndpoint()
                                    .customUserType(CustomOAuth2User.class, "teemo")
                                    .userService(this.oauth2UserService());
    }

    private OAuth2UserService<OAuth2UserRequest, OAuth2User> oauth2UserService() {
        return new CustomOAuth2UserService();
    }

下面是我的沉香树。

  • 百里香
    <div sec:authorize="isAuthenticated()">
        Text visible only to authenticated users.

        Authenticated username:
        <div sec:authentication="name"></div>

        <div sec:authorize="hasRole('USER')">hasRole('USER')</div>
        <div sec:authorize="hasRole('ROLE_USER')">hasRole('ROLE_USER')</div>
        <div sec:authorize="hasRole('ADMIN')">hasRole('ADMIN')</div>
        <div sec:authorize="hasRole('ROLE_ADMIN')">hasRole('ROLE_ADMIN')</div>
        <!-- TRUE -->
        <div sec:authorize="hasAuthority('USER')">hasAuthority('USER')</div>
        <div sec:authorize="hasAuthority('ROLE_USER')">hasAuthority('ROLE_USER')</div>
        <!-- TRUE -->
        <div sec:authorize="hasAuthority('ADMIN')">hasAuthority('ADMIN')</div>
        <div sec:authorize="hasAuthority('ROLE_ADMIN')">hasAuthority('ROLE_ADMIN')</div>
    </div>

    <div sec:authorize="!isAuthenticated()">Text visible only to
                unauthenticated users.
    </div>

下面是结果。

Text visible only to authenticated users. Authenticated username:
admin
hasAuthority('USER')
hasAuthority('ADMIN')

任何喜欢我挖掘的人,我希望对这个问题和答案有所帮助。

 类似资料:
  • 以下是不同的情况: 我的微服务A接收通过API网关并携带有效访问令牌的最终用户发起的请求(JWT,其作用域/声明对应于最终用户:用户名、id、电子邮件、权限等)。这个案子没问题。微服务拥有处理请求的所有信息。 第一个问题:如果微服务A需要调用微服务B会发生什么? null null 例如,微服务B中被调用的方法需要用户id才能工作。用户id值可以设置为查询字符串。如果使用用户访问令牌调用该方法,则

  • 我们希望使用外部身份提供者将现有的Spring Security项目从自定义用户名/密码实现(UserDetailsService等)迁移到oauth2登录。 但是,当通过外部提供程序登录时,安全上下文中的身份验证对象是一个,主体是一个。 由于我们在应用程序中使用的都是自定义身份验证主体,因此我们希望将转换为自定义对象。 对于oauth2资源服务器,似乎有一个API可让您将JWtAuthentia

  • 在JWT配置中,我不想做任何花哨的事情,只是想让它暂时通过: 我得到以下异常: 我对此的理解是:看起来,当Google发布访问令牌时,授权服务器(作为Google OAuth的客户机)试图将访问令牌解码为JWT,并抛出异常,因为Google的令牌不是有效的JWT(它只是一个访问令牌)。

  • OAuth2 JWT 配置文件引入了将 JWT 用作授权授予和客户端身份验证的可能性。 JWT客户端身份验证功能独立于特定的授权类型,并且可以与任何授权类型一起使用,也可以与客户端凭据授权一起使用。 但是,使用 JWT 授权类型似乎与将客户端凭据授予与 JWT 客户端身份验证结合使用完全相同,只是语法略有不同。 在这两种情况下,客户端都会联系令牌终结点以获取访问令牌: vs

  • 我正在尝试使用Spring Boot和OAuth2进行概念验证。我设立了一些项目,与这里概述的项目非常相似: https://spring.io/guides/tutorials/spring-boot-oauth2/ 这里:https://spring.io/guides/tutorials/spring-security-and-angular-js/ 与我的主要区别是我遗漏了所有Angula