OAuth は、Web サイトに保存されているユーザーのプライベート リソース (アバター、写真、ビデオなど) にサードパーティのアプリケーションがアクセスできるようにするオープン スタンダードです。プロセス中にサードパーティのアプリケーションにユーザー名とパスワードを提供する必要はありません。
特定のサービス プロバイダーに保存されているデータへのアクセスは、ユーザー名とパスワードの代わりにトークンを使用して実現されます。各トークンは、特定の Web サイトが特定の期間、特定のリソースにアクセスすることを許可します。
OAuth を使用すると、ユーザーはサードパーティの Web サイトが、すべての情報ではなく、他のリソース サーバーに保存されている特定の情報に柔軟にアクセスできるようになります。たとえば、ユーザーが QQ を通じて Zhihu にログインしたい場合、Zhihu はサードパーティ アプリケーションです。Zhihu がユーザーの基本情報にアクセスしたい場合は、ユーザーの承認が必要です。ユーザーが Zhihu に自分の QQ ユーザーを伝えた場合、名前とパスワードを指定すると、Zhihu はユーザーのすべてのデータにアクセスでき、ユーザーだけがパスワードを変更して権限を取り消すことができます。この認証方法には大きなセキュリティ上のリスクがあります。OAuth を使用すると、この問題はうまく解決できます。
トークンを使用すると、ユーザーはサードパーティ アプリケーションからのアクセス許可を柔軟に承認または取り消すことができます。 OAuth 2 は OAuth プロトコルの次のバージョンですが、OAuth 1.0 との下位互換性はありません。
OAuth 2 は、Web アプリケーション、デスクトップ アプリケーション、モバイル デバイス、リビング ルーム デバイスに特化した認証プロセスを提供しながら、クライアント開発者の簡素化に重点を置いています。従来の Web 開発のログイン認証は一般にセッションに基づいていますが、フロントエンドとバックエンドの分離されたアーキテクチャでセッションを使用し続けると、モバイル端末 (Android、IOS、WeChat アプレットなど) が認証を行わないため、多くの不便が生じます。 Cookie (WeChat アプレット) をサポートしていないと非常に不便ですが、OAuth 2 認証を使用することで解決できます。
まず、OAuth 2 のいくつかの基本的な役割を理解します。
リソース所有者: つまり、アバター、写真、ビデオなどを持つユーザーです。リソース
クライアント: つまり、サードパーティのアプリケーション
認可サーバー: ユーザーが提供した情報が正しいかどうかを確認するために使用されます。修正してトークンを返す サードパーティ アプリケーションの場合
実践
1. プロジェクトを作成し、依存関係を追加します
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <exclusions> <exclusion> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth3</artifactId> <version>2.3.3.RELEASE</version> </dependency>
Spring Boot の OAuth プロトコルは基本的に完成しているので、 Spring Security の依存関係を追加する必要があるので、まず Spring Security の依存関係を追加する必要がありますが、これには OAuth 2 が必要なので、OAuth 2 関連の依存関係を追加します トークンは Redis キャッシュ サーバーに保存できます 同時に、Redis には有効期限などの機能がありますトークンの保管に非常に適しているため、Redis も追加されています。
構成 application.properties
spring.redis.database=0
spring.redis.host=ip地址
spring.redis.port=6379
spring.redis.password=root
spring.redis.jedis.pool.max-active=8
spring.redis.jedis.pool.max-idle=8
spring.redis.jedis.pool.max-wait=-1ms
spring.redis.jedis.pool.min-idle=0
授权服务器和资源服务器可以是同一台服务器,也可以是不同服务器,此处假设是同一台服务器,通过不同的配置分别开启授权服务器和资源服务器,首先是授权服务器:
@Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired AuthenticationManager authenticationManager; @Autowired RedisConnectionFactory redisConnectionFactory; @Autowired UserDetailsService userDetailsService; @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("password") .authorizedGrantTypes("password", "refresh_token") .accessTokenValiditySeconds(1800) .resourceIds("rid") .scopes("all") .secret("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory)) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); } }
代码解释:
自定义类继承自 AuthorizationServerConfigurerAdapter ,完成对授权服务器的配置,然后通过 @EnableAuthorizationServer 注解开启授权服务器
注入 AuthenticationManager 用来支持 password 模式
注入 RedisConnectionFactory 用来完成 Redis 缓存,将令牌信息储存到 Redis 缓存中
注入 UserDetailsService 该对象为刷新 token 提供支持
在 configure(ClientDetailsServiceConfigurer clients) 方法中配置 password 授权模式,authorizedGrantTypes 表示 OAuth 2 中的授权模式为 password 和 refresh_token 两种,在标准的 OAuth 2 协议中,授权模式并不包括 refresh_token ,但是在 Spring Security 的实现中将其归为一种,因此如果要实现 access_token 的刷新,就需要添加这样一种授权模式;accessTokenValiditySeconds 方法配置了 access_token 的过期时间;resourceIds 配置了资源 id;secret 方法配置了加密后的密码,明文是 123
configure(AuthorizationServerEndpointsConfigurer endpoints) 方法配置了令牌的存储,AuthenticationManager 和 UserDetailsService 主要用于支持 password 模式以及令牌的刷新
configure(AuthorizationServerSecurityConfigurer security) 方法配置表示支持 client_id 和 client_secret 做登录认证
@Configuration @EnableResourceServer public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("rid").stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .antMatchers("/user/**").hasRole("user") .anyRequest().authenticated(); } }
代码解释:
自定义类继承自 ResourceServerConfigurerAdapter ,并添加 @EnableResourceServer 注解开启资源服务器配置
resources.resourceId(“rid”).stateless(true); 配置资源 id,这里的资源 id 和授权服务器中的资源 id 一直,然后设置这些资源仅基于令牌认证
configure(HttpSecurity http) 方法配置 HttpSecurity
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Bean @Override protected UserDetailsService userDetailsService() { return super.userDetailsService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("admin") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("admin") .and() .withUser("sang") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("user"); } @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/oauth/**").authorizeRequests() .antMatchers("/oauth/**").permitAll() .and().csrf().disable(); } }
这里两个 Bean 将注入授权服务器配置类中使用,另外,这里的 HttpSecurity 配置主要是配置 /oauth/** 模式的 URL ,这一类的请求直接放行。在 Spring Security 配置和资源服务器配置中,一共涉及两个 HttpSecurity ,其中 Spring Security 中的配置优先级高于资源服务器中的配置,即请求地址先经过 Spring Security 的 HttpSecurity ,再经过资源服务器的 HttpSecurity。
首先创建三个简单的请求地址
@RestController public class HelloController { @GetMapping("/admin/hello") public String admin() { return "Hello admin!"; } @GetMapping("/user/hello") public String user() { return "Hello user!"; } @GetMapping("/hello") public String hello() { return "hello"; } }
根据前文的配置,要请求这三个地址,分别需要 admin 角色、user 角色以及登录后访问。
所有都配置完成后,启动 Redis 服务器,再启动 Spring Boot 项目,首先发送一个 POST 请求获取 token,请求地址如下(注意这里是一个 POST 请求,为了显示方便,将参数写在地址栏中):http://localhost:8080/oauth/token?username=sang&password=123&grant_type=password&client_id=password&scope=all&client_secret=123
请求地址中包含的参数有用户名、密码、授权模式、客户端 id 、scope 以及客户端密码,基本就是授权服务器中所配置的数据,请求结果如图
其中 access_token 是获取其它资源时要用的令牌,refresh_token 用来刷新令牌,expires_in 表示 access_token 过期时间,当 access_token 过期后,使用 refresh_token 重新获取新的 access_token (前提是 refresh_token 未过期),请求地址(注意也是POST请求):http://localhost:8080/oauth/token?grant_type=refresh_token&refresh_token=693b0e36-4515-442a-8c5d-90bade3c74d2&client_id=password&client_secret=123
获取新的 access_token 时需要携带上 refresh_token ,同事授权模式设置为 refresh_token ,在获取的结果中 access_token 会变化,同时 access_token 有效期也会变化,如图
接下来访问所有资源,携带上 access_token 参数即可,例如 /user/hello 接口:http://localhost:8080/user/hello?access_token=0497e4bc-df37-460e-8755-b813b9dbf36a,访问结果如图
#リソースが不正にアクセスされた場合、たとえば、sang ユーザーが /admin/hello インターフェイスにアクセスした場合、結果は図に示すとおりになります。 ##この時点で、パスワード モード OAuth 認証システムが正常に確立されます。 OAuth には 4 つの認証モードがあり、開発者は開発の実情に応じていずれかを選択する必要がありますが、ここではフロントエンドとバックエンドを分離するアプリケーションで一般的に使用されるパスワード モードを紹介します。他の認証モードも同様であり、それぞれに独自の使用シナリオがあります。 全体として、Spring Security OAuth 2 の使用はまだ比較的複雑であり、設定も比較的面倒ですが、開発者のアプリケーション シナリオが比較的単純であれば、認可に従って独自の OAuth 2 認証システムを構築できます。ここで紹介するプロセスです。以上がSpringBootセキュリティ管理のOAuth2フレームワークの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。