当前位置:实例文章 » 其他实例» [文章]No3.深入学习用户权限和客户端权限的保存与使用,并实现授权服务端动态鉴权(属于security鉴权范围,不是oauth2的授权范围);

No3.深入学习用户权限和客户端权限的保存与使用,并实现授权服务端动态鉴权(属于security鉴权范围,不是oauth2的授权范围);

发布人:清晨敲代码 发布时间:2022-12-09 10:07 阅读次数:3

代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客

目录

A1.整理用户权限和客户端权限的保存与使用逻辑

B1.程序中的权限与最终返回的权限

C1.程序中持久化的用户信息是:

C2.程序中持久化的客户端信息是:

C3.用此客户端信息和用户信息登录后返回的token信息是:

C4.OAuth2ClientAuthenticationFilter过滤器认证的客户端信息

C5.OAuth2TokenEndpointFilter过滤器认证的用户信息

C6.OAuth2TokenEndpointFilter过滤器认证的授权token信息

C7.OAuth2TokenIntrospectionEndpointFilter过滤器认证的自省认证的信息

C8.BearerTokenAuthenticationFilter过滤器认证的token认证的信息

A2.资源鉴权的三种方式

B1.基于注解的接口访问权限(资源服务端)

C1.开启 @EnableGlobalMethodSecurity,并添加 @PreAuthorize注解

C2.PigCustomOpaqueTokenIntrospector

C3.PermissionService

B2.基于过滤器的动态访问权限(后期补充~)


前两篇文章的学习中,有涉及到用户权限这一块儿,但是我没仔细记住,本来想开发动态鉴权呢,但是觉得对用户权限这一块儿还不是很熟悉,所以在根据授权服务端流程再巩固一下!!!然后实现动态鉴鉴权。

A1.整理用户权限和客户端权限的保存与使用逻辑

我打算先从头尾两处看起,首先先确认用户都有哪些权限,客户端都有哪些权限,然后看登陆成功的token信息中都返回了哪些权限,看先两处对比的格式。

然后在其代码里面查找处理逻辑,首先客户端认证时会处理客户端权限,用户认证时会处理用户权限,再去这两个地方查看逻辑。

最终,再把这些串起来。

B1.程序中的权限与最终返回的权限

C1.程序中持久化的用户信息是:

    @Bean
    UserDetailsService userDetailsService(){
 
        UserDetails userDetails = User.builder()
                .username("qc")
                .password("123")
                .passwordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder()::encode)
                .roles("read","write")
                .build();
        //new一个用户管理业务,注入一盒用户信息
        return new InMemoryUserDetailsManager(userDetails);
    }

C2.程序中持久化的客户端信息是:

    private RegisteredClient createRegisteredClient(final String id) {
        return RegisteredClient.withId(UUID.randomUUID().toString())
//               客户端ID和密码
                .clientId("qingchen")
//               此处为了避免频繁启动重复写入仓库
                .id(id)
//                client_secret_basic    客户端需要存明文   服务器存密文
                .clientSecret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("secret"))
//                名称 可不定义
                .clientName("qingchen")
//                其它Scope
                .scope("server")
                .build();
    }
 

C3.用此客户端信息和用户信息登录后返回的token信息是:

{
    "sub": "qc",
    "clientId": "qingchen",
    "iss": "https://pig4cloud.com",
    "token_type": "Bearer",
    "access_token": "qingchen::qc::b421f83e-8858-414f-96ad-f700e41fcafc",
    "refresh_token": "fBo4y6sm6RpWcgB8rR00kW-m1l8w5djNH9jRlr19-LzLVzYrwyRG4yWNzcKO6qxLaeL87AKk0Lal414nrsZqfT8FKgDo4njMmDCLfjUdm4gEwIX6GKQAsHj_n3ov9V_y",
    "aud": [
        "qingchen"
    ],
    "license": "https://pig4cloud.com",
    "nbf": 1664164585.066,
    "user_info": {
        "password": null,
        "username": "qc",
        "authorities": [
            {
                "authority": "ROLE_read"
            },
            {
                "authority": "ROLE_write"
            }
        ],
        "accountNonExpired": true,
        "accountNonLocked": true,
        "credentialsNonExpired": true,
        "enabled": true
    },
    "scope": [
        "server"
    ],
    "exp": 1664168185.066,
    "expires_in": 3599,
    "iat": 1664164585.066,
    "jti": "55373257-827c-4222-959a-2505a13b9394"
}

C4.OAuth2ClientAuthenticationFilter过滤器认证的客户端信息

C5.OAuth2TokenEndpointFilter过滤器认证的用户信息

C6.OAuth2TokenEndpointFilter过滤器认证的授权token信息

C7.OAuth2TokenIntrospectionEndpointFilter过滤器认证的自省认证的信息

C8.BearerTokenAuthenticationFilter过滤器认证的token认证的信息

A2.资源鉴权的三种方式

通常有三种方式可以实现资源鉴权:1.基于配置的接口访问权限;2.基于注解的接口访问权限;3.基于过滤器的动态访问权限;

1.基于配置的接口访问权限,这个就类似前两篇里面,过滤链加的路径配置,可以设置为permit(),可以设置为authenticated(),也可以添加hasRole()。但是这样是在代码里面配置的,而且不够灵活。

2.基于注解的接口访问权限,这个是在接口处添加注解配置的,在过滤链中只会对需要认证的请求校验是否已认证,然后分发执行接口前会先调用注解对应的鉴权操作,检验是否有权限,有则执行接口,无则返回鉴权不通过。security提供了基础的鉴权操作,我们也可以实现自定义的鉴权操作。这样的鉴权非常容易理解,而且只需要给需要鉴权的接口添加注解就可以,但是,这样的方式还是不够灵活,基于编程的静态方式,具有一定的局限性。(本篇着重实现这一部分)

3.基于过滤器的动态访问权限,前面两种方式都有局限性,无法实现管理人员动态的配置和分配权限的。而动态配置就是动态处理了权限(角色)与资源(可以理解为接口URL)的映射关系,比如我们可以存储一个资源表,表中有资源URI、权限标识;我们会给一个用户某些角色/权限,角色会对应权限(用户也可以直接对应权限),然后对应到权限关联的资源URL。当登录用户访问某资源(某接口)时,先拿到该资源所对应的权限,然后在判断是否包含在当前用户拥有的权限里,有就可以访问,无就无权访问。

B1.基于注解的接口访问权限(资源服务端)

C1.开启 @EnableGlobalMethodSecurity,并添加 @PreAuthorize注解

基于注解的方式很简单,我们采用基于表达式进行方法访问控制。只需要开启全局基于注解的安全功能,然后在接口上添加注解即可;

我们首先在启动类上添加@EnableGlobalMethodSecurity( prePostEnabled = true)注解,然后在AuthenticatedController类中新建一个read方法,然后添加注解@PreAuthorize("hasAnyAuthority('ROLE_read')"),设置为拥有ROLE_read的权限的认证用户就可以访问。

然后我们启动访问/read,发现返回了403!

原因在于,我们通过accesstoken拿到的Authentication值,是默认转化的数据:

此时authorities里面存储的不是用户的权限,而是客户端的权限!

那么我们需要怎么做呢?有两种,看业务需要:

1.更改token认证后生成的authentication,实现自定义的token信息转化类生成BearerTokenAuthentication,这样就可以保证authorities属性是用户的权限;

2.自定义一个权限校验方式,通过注解鉴权时,走我们的自定义鉴权方式。

当然,我们也可以两种结合着实现,这样既能保证Authentication是我们想要的格式又能保证鉴权格式也是我们想要的!

C2.PigCustomOpaqueTokenIntrospector

首先先修改第一点,核心是在OpaqueTokenIntrospector接口下,通过token拿到用户信息的,我们就实现这个接口,原理可以仿照SpringOpaqueTokenIntrospector:

    @Override
    public OAuth2AuthenticatedPrincipal introspect(String token) {
        //将token转化成请求实体
        //使用RestOperations调用请求实体,并拿到响应实体
        //获取响应实体里面的claims
        //将claims转化成AuthenticatedPrincipal
        return this.convertClaimsSet(claims);
    }
    private OAuth2AuthenticatedPrincipal convertClaimsSet(Map<String, Object> claims) {
        ...
        Collection<GrantedAuthority> authorities = new ArrayList();
        Iterator var4 = ((List)((Map)claims.get("user_info")).get("authorities")).iterator();
 
        while(var4.hasNext()) {
            Map scope = (Map)var4.next();
            authorities.add(new SimpleGrantedAuthority((String) scope.get("authority")));
        }
        return new OAuth2IntrospectionAuthenticatedPrincipal(claims, authorities);
    }

重点就是this.convertClaimsSet()方法,我们只修改authorities就行,按照token里返回的格式获取!

最终的BearerTokenAuthentication的属性authorities已存储为用户权限:

此时,我们直接执行程序访问就可以访问成功啦!!!

C3.PermissionService

由于系统默认给的@PreAuthorize("hasAnyAuthority('ROLE_read')")注解不好扩展,我们可以自定义一个校验方式,先创建一个校验的业务类,提供一个校验方法,然后使用该注解@PreAuthorize("@校验业务类beanName.方法名称('所需权限值')")。那么当请求该接口时,回先进入该方法进行校验返回的是true才会执行接口,否则返回403;

public class PermissionService {
 
    /**
     * @Description: 判断接口是否有任意xxx,xxx权限
     * @param permissions
     * @Return: boolean
     */
    public boolean hasPermission(String... permissions) {
        //判断所需权限是否为空,为空则直接返回false
        //拿到请求中已认证用户拥有的权限,若为null则直接返回false
        //判断拥有的权限中是否有所需权限,若有则返回true,否则返回false
 
        return ;
    }
}

记得在PigResourceServerAutoConfiguration里面注入该类的的bean哦

访问成功!!

B2.基于过滤器的动态访问权限(后期补充~)

之前写过一篇关于security动态鉴权的,记录一下:spring security——学习笔记(day06)-实现授权认证-FilterSecurityInterceptor、SecurityMetadataSource、AccessDecisionM_清晨敲代码的博客-CSDN博客

这里就不再补充了,不过,我刚刚又发现,其实我们完全可以在PermissionService类里面添加全局角色与资源映射关系,然后注解中参数不再传递所需权限,而是传递当前接口唯一标识(也就是资源唯一标识,例如访问路径),然后在鉴权方法里面根据资源唯一标识拿到持久化的所需权限,然后再与已认证用户的权限进行对比!

这样就可以不用自定义那么多过滤器类了呀,不过我并没有发现有项目是这样的鉴权逻辑,改天试一下性能!!

相关标签:

免责声明

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱290110527@qq.com删除。

Top