springsecurity使用(三)认识OAuth2

232 阅读6分钟

springsecurity使用(三)认识OAuth2

一、认识OAuth2

OAuth2(Open Authorization 2.0)是一种用于授权的开放标准协议。它允许用户通过授权第三方应用程序来访问他们在另一个服务提供者上存储的信息,而无需将其用户名和密码提供给第三方应用程序。

OAuth2的设计目标是为了解决用户在使用第三方应用程序时,需要提供自己的用户名和密码,从而引发安全风险和隐私问题的情况。通过OAuth2,用户可以选择仅向授权服务器提供访问权限,而不是直接提供用户名和密码。

在OAuth2的体系结构中,有以下几个主要角色:

  1. 资源所有者(Resource Owner):拥有受保护资源的用户,可以是一个人或一个应用程序。
  2. 客户端(Client):要访问资源的第三方应用程序,需要得到资源所有者的授权。
  3. 授权服务器(Authorization Server):负责验证资源所有者的身份,并颁发访问令牌给客户端。
  4. 资源服务器(Resource Server):保护受限资源,只接受有效的访问令牌。

OAuth2的核心流程如下:

  1. 客户端向资源所有者请求授权,获取授权许可。
  2. 资源所有者同意授权,并将授权许可发送给客户端。
  3. 客户端使用授权许可向授权服务器请求访问令牌。
  4. 授权服务器验证授权许可,并颁发访问令牌给客户端。
  5. 客户端使用访问令牌向资源服务器请求受限资源。
  6. 资源服务器验证访问令牌的有效性,并返回受限资源给客户端。

通过OAuth2,用户可以在不直接提供用户名和密码的情况下,安全地将其资源分享给第三方应用程序。它提供了一种安全且灵活的授权机制,广泛应用于Web和移动应用程序的身份验证和授权场景中。

二、授权服务器和资源服务器

OAuth2是一种授权框架,用于保护Web API等资源。在OAuth2中,有两种主要的角色:客户端和资源所有者。客户端需要向资源服务器请求访问资源,而资源所有者需要授权客户端对其资源进行访问。

为了实现OAuth2的授权流程,通常需要使用两个服务器:授权服务器和资源服务器。授权服务器负责颁发访问令牌,并验证客户端的身份和权限,而资源服务器则负责保护受保护的资源,并验证访问令牌的有效性。

因此,为了实现基于OAuth2的认证和授权功能,需要配置授权服务器和资源服务器。资源服务器需要验证访问令牌的有效性,并确保只有经过授权的客户端可以访问资源。资源服务器还需要与授权服务器交互,以获取访问令牌并验证其有效性。

资源服务器和授权服务器之间的分离可以提高系统的安全性,并简化系统的设计和管理。资源服务器只需要专注于资源的保护,而授权服务器则负责授权和令牌的管理。这样可以将系统功能分离,提高系统的可维护性和扩展性。

三、实现OAuth2

当涉及到实现OAuth2的功能时,通常会使用一些现成的库来简化开发过程。在Java语言中,有许多开源的OAuth2库可供选择,例如Spring Security OAuth、Apache Oltu、Keycloak等。这些库提供了OAuth2的实现细节,可以帮助你快速构建OAuth2的功能。

下面我将演示如何使用Spring Security OAuth库来实现OAuth2的功能:

1、添加依赖:在你的Java项目中,需要添加Spring Security OAuth的依赖。你可以在Maven配置文件中添加以下依赖:

```xml
<dependency>
    <groupId>org.springframework.security.oauth</groupId>
    <artifactId>spring-security-oauth2</artifactId>
    <version>2.4.0</version>
</dependency>
```

2、实现授权服务器

(1)、服务目录结构

image.png (2)、导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>mayiktTestOauth</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mayiktTestOauth</name>
    <description>mayiktTestOauth</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- springboot整合freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!-->spring-boot 整合security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- Spring Security OAuth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

(3)、SecurityConfig.java 认证配置类

@Component
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 需要填写 认证账户  mayikt
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.
                inMemoryAuthentication()
                .withUser("mayikt")
                .password(passwordEncoder().encode("mayikt"))
                .authorities("/*");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated() //所有请求都需要通过认证
                .and()
                .httpBasic() //Basic登录
                .and()
                .csrf().disable(); //关跨域保护
    }
}

(4)、AuthorizationConfig.java 认证授权Server端

/**
 * 认证授权Server端
 */
@Component
@EnableAuthorizationServer
public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        //允许表单提交
        security.allowFormAuthenticationForClients()
                .checkTokenAccess("permitAll()");
    }

    /**
     * appid mayikt secret= 123456
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                // appid
                .withClient("mayikt")
                // appsecret
                .secret(passwordEncoder.encode("123456"))
                // 授权码
                .authorizedGrantTypes("authorization_code")
                // 作用域
                .scopes("all")
                // 资源的id
                .resourceIds("mayikt_resource")
                // 回调地址
                .redirectUris("http://www.mayikt.com/callback");
    }
}

3、实现资源服务器

(1)、导入依赖 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.3</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>mayikt-resources</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>mayikt-resources</name>
    <description>mayikt-resources</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <!-- SpringBoot整合Web组件 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- springboot整合freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!-->spring-boot 整合security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- Spring Security OAuth2 -->
        <dependency>
            <groupId>org.springframework.security.oauth</groupId>
            <artifactId>spring-security-oauth2</artifactId>
            <version>2.2.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

(2)、配置yaml资源

# 资源服务器
mayikt:
  appid: mayikt
  appsecret: 123456
server:
  port: 8083

(3)、ResourceConfig.java 资源Server端

/**
 * 资源Server端
 */
@Configuration
@EnableResourceServer
public class ResourceConfig extends ResourceServerConfigurerAdapter {

    @Value("${mayikt.appid}")
    private String mayiktAppId;
    @Value("${mayikt.appsecret}")
    private String mayiktAppSecret;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Primary
    @Bean
    public RemoteTokenServices remoteTokenServices() {
        final RemoteTokenServices tokenServices = new RemoteTokenServices();
        //设置授权服务器check_token端点完整地址
        tokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token"); // 授权服务器链接
        //设置客户端id与secret,注意:client_secret值不能使用passwordEncoder加密!
        tokenServices.setClientId(mayiktAppId);
        tokenServices.setClientSecret(mayiktAppSecret);
        return tokenServices;
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //设置创建session策略
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED);
        //@formatter:off
        //所有请求必须授权
        http.authorizeRequests()
                .anyRequest().authenticated();
        //@formatter:on
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) {
        resources.resourceId("mayikt_resource").stateless(true);
    }
}

(4)、MemberService.java 资源服务

@RestController
public class MemberService {
    @GetMapping("/getMember")
    public String getMember() {
        return "我是会员服务接口";
    }
}

其它 测试

1、获取code

http://localhost:8080/oauth/authorize?client_id=mayikt&response_type=code

image.png

image.png

2、获取accessToken

http://localhost:8080/oauth/token?code=0ns7B9&grant_type=authorization_code&redirect_uri=http://www.mayikt.com/callback&scope=all image.png

返回值

{
    "access_token": "86661ffb-1da8-4df6-9c5c-e2d5e6bdb075",
    "token_type": "bearer",
    "expires_in": 43199,
    "scope": "all"
}

3、访问资源服务器

image.png

4、检查token是否过期

http://localhost:8080/oauth/check_token?token=86661ffb-1da8-4df6-9c5c-e2d5e6bdb075

image.png

返回值

{
    "aud": [
        "mayikt_resource"
    ],
    "user_name": "mayikt",
    "scope": [
        "all"
    ],
    "active": true,
    "exp": 1699146930,
    "authorities": [
        "/*"
    ],
    "client_id": "mayikt"
}

5、刷新token

http://localhost:8080/oauth/check_token?grant_type=refresh_token&refresh_token=86661ffb-1da8-4df6-9c5c-e2d5e6bdb075&client_id=mayikt&client_secret=12345

image.png