SpringSecurity整合OAuth2

一、概述

1.1 OAuth2.0 是什么?

OAuth(Open Authorization)是一个开放标准,允许用户让第三方应用访问该用户在某一网站上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。
每一个令牌授权一个特定的网站(例如,视频编辑网站)在特定的时段(例如,接下来的 2 小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth 让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容。OAuth2.0 是用于授权的行业标准协议。OAuth2.0 为简化客户端开发提供了特定的授权流,包括 Web 应用、桌面应用、移动端应用等。

1.2 OAuth2.0 角色解释

OAuth2.0 中,有如下角色:

  • Authorization Server:认证服务器,用于认证用户。如果客户端认证通过,则发放访问资源服务器的令牌。
  • Resource Server:资源服务器,拥有受保护资源。如果请求包含正确的访问令牌,则可以访问资源。
  • Client:客户端。它请求资源服务器时,会带上访问令牌,从而成功访问资源。
  • Resource Owner:资源拥有者。最终用户,他有访问资源的账号与密码。
1.3 OAuth 2.0 运行流程

OAuth 2.0 的授权码模式的运行流程:
在这里插入图片描述

  • (A)用户打开客户端以后,客户端要求用户给予授权。
  • (B)用户同意给予客户端授权。
  • (C)客户端使用上一步获得的授权,向认证服务器申请令牌。
  • (D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
  • (E)客户端使用令牌,向资源服务器申请获取资源。
  • (F)资源服务器确认令牌无误,同意向客户端开放资源。
    上述的六个步骤,B 是关键,即用户如何给客户端进行授权。有了授权之,客户端就可以获取令牌,进而凭令牌获取资源。
1.4 OAuth 2.0 授权模式

客户端必须得到用户的授权(Authorization Grant),才能获得访问令牌(Access Token)。
OAuth2.0 定义了四种授权方式:

  • 授权码模式(Authorization Code
  • 密码模式(Resource Owner Password Credentials
  • 简化模式(Implicit
  • 客户端模式(Client Credentials

其中,密码模式和授权码模式比较常用。

二、密码模式

2.1 搭建授权服务器
2.1.1 引入依赖
<dependencies>
    <!-- 实现对 Spring MVC 的自动配置 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- 实现对 Spring Security OAuth2 的自动配置 -->
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    </dependency>
</dependencies>

添加 spring-security-oauth2-autoconfigure 依赖,引入 Spring Security OAuth 并实现自动配置。同时,它也引入了 Spring Security 依赖。

2.1.2 SecurityConfig

创建 SecurityConfig 配置类,提供一个账号密码为「canying/123456」的用户。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.
                // 使用内存中的 InMemoryUserDetailsManager
                        inMemoryAuthentication()
                // 不使用 PasswordEncoder 密码编码器
                .passwordEncoder(passwordEncoder())
                // 配置 canying 用户
                .withUser("canying").password("123456").roles("USER");
    }

}
2.1.3 OAuth2AuthorizationServerConfig

创建 OAuth2AuthorizationServerConfig 配置类,进行授权服务器。

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * 用户认证 Manager
     */
    @Autowired
    private AuthenticationManager authenticationManager;
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager);
    }
    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        oauthServer.checkTokenAccess("isAuthenticated()");
    }
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // <4.1>
                .withClient("clientapp").secret("112233") // <4.2> Client 账号、密码。
                .authorizedGrantTypes("password") // <4.2> 密码模式
                .scopes("read_userinfo", "read_contacts") // <4.2> 可授权的 Scope
//                .and().withClient() // <4.3> 可以继续配置新的 Client
        ;
    }
2.1.4 AuthorizationServerApplication
@SpringBootApplication
public class AuthorizationServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class,args);
    }
}
2.1.5 简单测试

POST 请求 http://localhost:8080/oauth/token 地址,使用密码模式进行授权
在这里插入图片描述
在这里插入图片描述
请求说明:

  • 通过 Basic Auth 的方式,填写 client-id + client-secret 作为用户名与密码,实现 Client 客户端有效性的认证。
  • 请求参数 grant_type"password",表示使用密码模式
  • 请求参数 usernamepassword,表示用户的用户名与密码。

响应说明:

  • 响应字段 access_token访问令牌,后续客户端在访问资源服务器时,通过它作为身份的标识。
  • 响应字段 token_type令牌类型,一般是 bearer 或是 mac 类型。
  • 响应字段 expires_in 为访问令牌的过期时间,单位为秒。
  • 响应字段 scope权限范围

POST 请求 http://localhost:8080/oauth/check_token 地址,校验访问令牌的有效性。
在这里插入图片描述

在这里插入图片描述

2.2 搭建资源服务器
2.2.1 引入依赖
<!-- 实现对 Spring MVC 的自动配置 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- 实现对 Spring Security OAuth2 的自动配置 -->
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
2.2.2 配置文件

创建 application.yml 配置文件,添加 Spring Security OAuth 相关配置。

server:
  port: 9090
security:
  oauth2:
    # OAuth2 Client 配置,对应 OAuth2ClientProperties 类
    client:
      client-id: clientapp
      client-secret: 112233
    # OAuth2 Resource 配置,对应 ResourceServerProperties 类
    resource:
      token-info-uri: http://127.0.0.1:8080/oauth/check_token # 获得 Token 信息的 URL
    # 访问令牌获取 URL,自定义的
    access-token-uri: http://127.0.0.1:8080/oauth/token

security.oauth2.client 配置项,OAuth2 Client 配置,对应 OAuth2ClientProperties 类。在这个配置项中,我们添加了客户端的 client-idclient-secret。为什么要添加这个配置项呢?因为资源服务器会调用授权服务器的 /oauth/check_token 接口,而考虑到安全性,我们配置了该接口需要进过客户端认证

security.oauth2.resource 配置项,OAuth2 Resource 配置,对应 ResourceServerProperties 类。这里,我们通过 token-info-uri 配置项,设置使用授权服务器的 /oauth/check_token 接口,校验访问令牌的有效性。

security.access-token-uri 配置项,是我们自定义的,设置授权服务器的 oauth/token 接口,获取访问令牌。因为稍后我们将在 LoginController中,实现一个 /login 登录接口。

2.2.3 OAuth2ResourceServerConfig
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                // 设置 /login 无需权限访问
                .antMatchers("/login").permitAll()
                // 设置其它请求,需要认证后访问
                .anyRequest().authenticated()
        ;
    }
}

① 在类上添加 @EnableResourceServer 注解,声明开启 OAuth 资源服务器的功能。同时,继承 ResourceServerConfigurerAdapter 类,进行 OAuth 资源服务器的配置。

#configure(HttpSecurity http) 方法,设置 HTTP 权限。这里,我们设置 /login 接口无需权限访问,其它接口认证后可访问。这样,客户端在访问资源服务器时,其请求中的访问令牌会被资源服务器调用授权服务器的 /oauth/check_token 接口,进行校验访问令牌的正确性。

2.2.4 ExampleController

创建 ExampleController 类,提供 /api/example/hello 接口,表示一个资源。

@RestController
@RequestMapping("/api/example")
public class ExampleController {
    @RequestMapping("/hello")
    public String hello() {
        return "world";
    }
}
2.2.5 ResourceServerApplication

创建 ResourceServerApplication 类,资源服务器的启动类。

@SpringBootApplication
public class ResourceServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ResourceServerApplication.class, args);
    }
}
2.2.6 简单测试

① 首先,请求 127.0.0.1:9090/api/example/hello 接口,不带访问令牌,则请求会被拦截。
在这里插入图片描述
② 然后,请求 127.0.0.1:9090/api/example/hello 接口,带上错误的访问令牌,则请求会被拦截。
在这里插入图片描述

  • 注意: 访问令牌需要在请求头 “Authorization” 上设置,并且以 "Bearer " 开头。

③ 最后,请求 127.0.0.1:9090/api/example/hello 接口,带上正确的访问令牌,则请求会被通过。
在这里插入图片描述

2.2.7 LoginController

创建 LoginController 类,提供 /login 登录接口。

@RestController
@RequestMapping("/")
public class LoginController {
    @Autowired
    private OAuth2ClientProperties oauth2ClientProperties;

    @Value("${security.oauth2.access-token-uri}")
    private String accessTokenUri;

    @PostMapping("/login")
    public OAuth2AccessToken login(@RequestParam("username") String username,
                                   @RequestParam("password") String password) {
        // <1> 创建 ResourceOwnerPasswordResourceDetails 对象
        ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
        resourceDetails.setAccessTokenUri(accessTokenUri);
        resourceDetails.setClientId(oauth2ClientProperties.getClientId());
        resourceDetails.setClientSecret(oauth2ClientProperties.getClientSecret());
        resourceDetails.setUsername(username);
        resourceDetails.setPassword(password);
        // <2> 创建 OAuth2RestTemplate 对象
        OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails);
        restTemplate.setAccessTokenProvider(new ResourceOwnerPasswordAccessTokenProvider());
        // <3> 获取访问令牌
        return restTemplate.getAccessToken();
    }

}

/login 接口中,资源服务器扮演的是一个 OAuth 客户端的角色,调用授权服务器的 /oauth/token 接口,使用密码模式进行授权,获得访问令牌

<1> 处,创建 ResourceOwnerPasswordResourceDetails 对象,填写密码模式授权需要的请求参数。

<2> 处,创建 OAuth2RestTemplate 对象,它是 Spring Security OAuth 封装的工具类,用于请求授权服务器。同时,将 ResourceOwnerPasswordAccessTokenProvider 设置到其中,表示使用密码模式授权。
<3> 处,调用 OAuth2RestTemplate 的 #getAccessToken() 方法,调用授权服务器的 /oauth/token 接口,进行密码模式的授权。

注意: OAuth2RestTemplate有状态的工具类,所以需要每次都重新创建。

2.2.8 简单测试

在这里插入图片描述

三、授权码模式

四、简化模式

五、客户端模式

待续…

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值