OAuth2 客户端主体在通过其他自定义授权服务器(SpringBoot2 和 OAuth2)进行身份验证时没有 GrantedAuthorities [英] OAuth2 Client Principal do not have GrantedAuthorities when authenticated by Other Custom Authorization Server (SpringBoot2 & OAuth2)

查看:34
本文介绍了OAuth2 客户端主体在通过其他自定义授权服务器(SpringBoot2 和 OAuth2)进行身份验证时没有 GrantedAuthorities的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Spring Boot2 作为框架,使用 Thymeleaf 作为模板引擎.

在我的授权服务器中,我将用户admin"添加为ROLE_ADMIN".

但在客户端应用程序中,当我以管理员"身份登录并从 SecurityContextHolder.getContext().getAuthentication(), Granted Authorities 打印 Authentication 对象时 属性只有ROLE_USER".

以下是我的授权服务器配置.

 @Overrideprotected void configure(AuthenticationManagerBuilder auth) 抛出异常 {授权.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN");授权.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()真的//获取权限()[ROLE_USER]//getPrincipal()名称:[admin],授予权限:[ROLE_USER],用户属性:[authorities=[{authority=ROLE_ADMIN}, {authority=ROLE_USER}],...

以下是我的百里香代码.

 

仅对经过身份验证的用户可见的文本.<!-- 主体名称-->认证用户名:<div sec:authentication="name"></div><div sec:authorize="hasRole('USER')">用户可见的文本.</div><!-- 我看不到这条消息--><div sec:authorize="hasRole('ADMIN')">管理员可见的文本.</div>经过身份验证的用户角色:<!-- 仅打印[ROLE_USER]"--><div sec:authentication="principal.authorities"></div>

<div sec:authorize="!isAuthenticated()">文本仅对未经身份验证的用户.

所以,我想访问 thymeleaf 中的 Principal.UserAttributes.authorities.

我指的是 OAuth2AuthenticationTokenOAuth2User.getAttributes()DefaultOAuth2User.toString()

我该怎么做?

解决方案

我解决了.

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

  • AuthorizationServer WebSecurityConfigurerAdapter 配置

@Configuration@启用网络安全公共类 SecurityConfig 扩展了 WebSecurityConfigurerAdapter {...@覆盖protected void configure(AuthenticationManagerBuilder auth) 抛出异常 {授权.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("USER", "ADMIN").authorities("USER", "ADMIN");授权.inMemoryAuthentication().withUser("user").password(passwordEncoder().encode("123")).roles("USER");}...}

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

  • ResourceServer /me 映射控制器

@RestController公共类用户控制器{@RequestMapping("/我")公共主体用户(主体主体){返还本金;}}

以下是我客户端的 WebSecurityConfigurerAdapter 配置

  • 客户端 WebSecurityConfigurerAdapter 配置

@Configuration@EnableOAuth2Client公共类 WebSecurityConfigurerAdapterImpl 扩展 WebSecurityConfigurerAdapter {@覆盖protected void configure(HttpSecurity http) 抛出异常 {http.csrf().disable().authorizeRequests().antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll().anyRequest().authenticated().and().oauth2Login();}

在客户端的控制器中,我是这样登录的.

  • 在客户端控制器中记录Principal

 @GetMapping("")公共字符串 git1() {身份验证 auth = SecurityContextHolder.getContext().getAuthentication();System.out.println(auth.getPrincipal());/** 百里香叶使用这个 **/Object authenticationProperty = AuthUtils.getAuthenticationProperty(auth, "principal.attributes['authorities']");System.out.println(authenticationProperty.toString());返回 VIEW_PATH + "git1";}

以下是结果

名称:[admin],授予权限:[ROLE_USER],用户属性:[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,认证=真,主体=管理员,凭证=不适用,名称=管理员},oauth2Request={clientId=foo,范围=[read],requestParameters={client_id=foo},resourceIds=[],authorities=[],批准=真,刷新=假,redirectUri=null,responseTypes=[],扩展={},refreshTokenRequest=null,grantType=null},clientOnly=false,principal=admin,credentials=,name=admin][{authority=USER}, {authority=ADMIN}]

如您所见,我在授权服务器中添加了ROLE_USER"和ROLE_ADMIN"权限.

在资源服务器的 Principal 对象中同时授予ROLE_ADMIN"和ROLE_USER".

但是在客户端的 Principal 对象中没有授予ROLE_ADMIN".只有ROLE_USER".

Principal.atttibutes['authorities'] 有 'USER', 'ADMIN'.

正如@Rahil Husain 所说,有 DefaultOAuth2UserService 并且此服务仅将ROLE_USER"授予 OAuth2User 对象.

首先,我通过 @Componenet 注释(也是 @Bean)向客户端添加了 CustomAuthoritiesExtractor.

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

所以,我实现了 CustomOAuth2UserCustomOAuth2UserService.

像这样.

  • CustomOAuth2User

public class CustomOAuth2User 实现 OAuth2User {私人列表当局;私有映射<字符串,对象>属性;私人字符串名称;public CustomOAuth2User(Listauthority, Map attributes) {this.authorities = 权限;this.attributes = 属性;}@覆盖公共收藏getAuthorities() {返回 this.authorities;}@覆盖公共地图<字符串,对象>获取属性(){如果(this.attributes == null){this.attributes = new HashMap<>();this.attributes.put("name", this.getName());}返回属性;}@覆盖公共字符串 getName() {返回 this.name;}公共无效集名称(字符串名称){this.name = 名称;}}

以下是CustomOAuth2UserService

  • CustomOAuth2UserService

public class CustomOAuth2UserService extends DefaultOAuth2UserService {@覆盖公共 OAuth2User loadUser(OAuth2UserRequest userRequest) 抛出 OAuth2AuthenticationException {OAuth2User oAuth2User = super.loadUser(userRequest);AuthoritiesExtractor authorityExtractor = new CustomAuthoritiesExtractor();列表grantAuthorityList = authorityExtractor.extractAuthorities(oAuth2User.getAttributes());CustomOAuth2User customOAuth2User = new CustomOAuth2User(grantedAuthorityList, oAuth2User.getAttributes());customOAuth2User.setName(oAuth2User.getName());返回 customOAuth2User;}}

以下是我的CustomAuthoritiesExtractor.此类未用作 @Bean@Component.直接在 CustomOAuth2Service 中用于映射 CustomOAuth2User 对象的权限

  • CustomAuthoritiesExtractor

public class CustomAuthoritiesExtractor 实现 AuthoritiesExtractor {@覆盖公共列表提取权限(地图<字符串,对象>地图){return AuthorityUtils.commaSeparatedStringToAuthorityList(asAuthorities(map));}private String asAuthorities(Map map) {列表<字符串>权限 = 新的 ArrayList<>();List>认证 =(List) map.get("authorities");for (LinkedHashMap entry : authz) {authority.add(entry.get("authority"));}return String.join(",", authority);}}

最后,我将客户端的端点更改为使用我的 CustomOAuth2UserCustomOAuth2UserService.

所以,我像这样更改了客户端的 WebSecurityConfigurerAdapter 配置.

@Configuration@EnableOAuth2Client公共类 WebSecurityConfigurerAdapterImpl 扩展 WebSecurityConfigurerAdapter {@覆盖protected void configure(HttpSecurity http) 抛出异常 {http.csrf().disable().authorizeRequests().antMatchers("/", "/home", "/error", "/webjars/**", "/resources/**", "/login**").permitAll().anyRequest().authenticated().and().oauth2Login()/** 添加这个配置**/.userInfoEndpoint().customUserType(CustomOAuth2User.class, "teemo").userService(this.oauth2UserService());}私有 OAuth2UserServiceoauth2UserService() {返回新的 CustomOAuth2UserService();}

下面是我的百里香.

  • 百里香叶

 

仅对经过身份验证的用户可见的文本.认证用户名:<div sec:authentication="name"></div><div sec:authorize="hasRole('USER')">hasRole('USER')</div><div sec:authorize="hasRole('ROLE_USER')">hasRole('ROLE_USER')

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

<div sec:authorize="!isAuthenticated()">文本仅对未经身份验证的用户.

以下是结果.

文本仅对经过身份验证的用户可见.认证用户名:行政hasAuthority('用户')hasAuthority('管理员')

任何和我一样挖掘的人,我希望对这个问题和答案有所帮助.

但我不知道这是事实上的标准方式.

刚刚..正在工作.

i'm using Spring Boot2 as Framework and Thymeleaf as template engine.

in my authorization server, i added user 'admin' as 'ROLE_ADMIN'.

but in Client Application, when i loged in as 'admin' and print Authentication Object from SecurityContextHolder.getContext().getAuthentication(), Granted Authorities property has only 'ROLE_USER'.

following is my authorization server config.

    @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");

    }

and following is Authentication Object from SecurityContextHolder.getContext().getAuthentication()'s logging code.

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

and result is

//  isAuthenticated()
true

// getAuthorites()
[ROLE_USER] 

// getPrincipal()
Name: [admin], Granted Authorities: [ROLE_USER], User Attributes: [authorities=[{authority=ROLE_ADMIN}, {authority=ROLE_USER}], ...

following is my thymeleaf code.

            <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>

so, i want to access Principal.UserAttributes.authorities in thymeleaf.

i'm refering OAuth2AuthenticationToken, OAuth2User.getAttributes() and DefaultOAuth2User.toString()

how can i do this?

解决方案

I solved.

In Authorization Server, i configed like this.

  • AuthorizationServer WebSecurityConfigurerAdapter config

@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");

    }
    ...
}

and following is my Resource Server's /me mapping controller

  • ResourceServer /me mapped Controller

@RestController
public class UserController {

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

and following is my Client's WebSecurityConfigurerAdapter config

  • Client WebSecurityConfigurerAdapter config

@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();
    }

and in Client's Controller, i logged like this.

  • logging Principal in Client Controller

    @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";
    }

and following is the result

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}]

as you can see, i added 'ROLE_USER' and 'ROLE_ADMIN' Authorities in Authorization Server.

in Resource Server's Principal Object granted both 'ROLE_ADMIN' and 'ROLE_USER'.

but in Client's Principal Object doesn't granted 'ROLE_ADMIN'. there is 'ROLE_USER' Only.

and Principal.atttibutes['authorities'] has 'USER', 'ADMIN'.

as @Rahil Husain said, there is DefaultOAuth2UserService and this service grant 'ROLE_USER' only to OAuth2User Object.

first, i added CustomAuthoritiesExtractor via @Componenet annotation (@Bean too.) to Client.

but this doesn't working in my projects.

so, i implemented CustomOAuth2User and CustomOAuth2UserService.

like this.

  • CustomOAuth2User

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;
    }

}

and following is CustomOAuth2UserService

  • 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;
    }
}

and following is my CustomAuthoritiesExtractor. this class not used as @Bean or @Component. directly used in CustomOAuth2Service for mapping CustomOAuth2User object's authorities

  • 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);
    }
}

and final, i changed Client's endpoint to using my CustomOAuth2User and CustomOAuth2UserService.

so, i changed Client's WebSecurityConfigurerAdapter config like this.

@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();
    }

and following is my thymeleaf.

  • thymeleaf

    <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>

and following is the result.

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

anyone who digging like me, i hope help this question and answers.

but i don't know this is de facto-standard way.

just.. working now.

这篇关于OAuth2 客户端主体在通过其他自定义授权服务器(SpringBoot2 和 OAuth2)进行身份验证时没有 GrantedAuthorities的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆