天天看点

2、oauth2授权

这篇文章我们来简单介绍下如何使用oauth的授权接口。oauth2为我们提供了认证服务和资源服务,认证服务需要引入@EnableAuthorizationServer来开启,资源服务需要引入 @EnableResourceServer来开启。 oauth2的授权接口是oauth/token,通过TokenEndpoint暴露该接口,而TokenEndpoint是通过注解@EnableAuthorizationServer引入的。下面我们通过一个简单的示例来了解一下oauth2的认证过程

1、引入依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.2.4.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

</dependencies>
           

2、创建Oauth2Config类,设置oauth的资源服务和认证服务相关配置。

@Configuration
public class Oauth2Config {

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

/**
 * 开启oauth2身份认证
 */
    @Configuration
    @EnableAuthorizationServer
    public static class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter{

        @Autowired
        private PasswordEncoder passwordEncoder;
        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
           //设置用户基本信息,比如scope、granttype、clientid、secret等
            clients.inMemory()
                    .withClient("client_1")
                    .secret(passwordEncoder.encode("secret_1"))
                    .scopes("select").authorities("aut")
                    .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password")
                    .redirectUris("http://www.baidu.com");
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.passwordEncoder(passwordEncoder)
            //开启表单认证。创建ClientCredentialsTokenEndpointFilter对请求auth/token拦截,并获取client_id和secret进行身份认证
            .allowFormAuthenticationForClients();
        }
    }

/**
 * 对所有的请求进行身份拦截验证,除了oauth开头的请求,资源服务配置类会为我们创建OAuth2AuthenticationProcessingFilter
 * 该拦截器会获取请求头Authorization并进行相关校验
 */
    @Component
    @EnableResourceServer
    public static class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            super.configure(http);
        }
    }
}
           

3、创建rest接口,用于请求测试。

@RestController
public class IndexController {
    @RequestMapping("api/index/{id}")
    public String index(@PathVariable String id) {
        return id;
    }
}
           

4、启动服务。我们先访问接口http://127.0.0.1:8080/api/index/1看请求是否被拦截。

{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}
           

直接访问的话,会返回认证错误的提示。

5、获取票据。我们可以通过oauth/token接口获取票据,通过grant_type参数指定获取token的授权类型,取值有client_credentials、password、authorization_code、refresh_token。

5.1、client_credentials

2、oauth2授权

grant_type:授权类型,用户client_1必须有相对应的授权权限

scope:作用域,用户client_1必须有相对应的scope

client_id:用户id

client_secret:用户密码

这时候我们在请求api的时候在请求头中添加Authorization,值为Bearer 87772dc0-2906-492f-bc73-c69e42d66605,Bearer后面的值就是我们刚刚获取的access_token。expires_in单位为秒,再次访问api,访问成功。

5.2、password。如果grant_type为密码,我们需要再配置AuthenticationManager和UserDetailsService

5.2.1、创建AuthenticationManager。新建类SecurityConfig,继承WebSecurityConfigurerAdapter,重写authenticationManager(),将该方法定义为一个bean

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }
}
           

5.2.2、创建UserDetailsService。新建类CustomUserService,继承UserDetailsService

@Component
public class CustomUserService implements UserDetailsService {

    @Resource
    private PasswordEncoder passwordEncoder;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User(username, passwordEncoder.encode(username), AuthorityUtils.createAuthorityList("user"));
    }
}
           

5.2.2、在oauth的认证服务中配置AuthenticationManager和UserDetailsService。

/**
     * 开启oauth2身份认证
     */
    @Configuration
    @EnableAuthorizationServer
    public static class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter{

        @Autowired
        private PasswordEncoder passwordEncoder;
        @Autowired
        private AuthenticationManager authenticationManager;
        @Autowired
        private UserDetailsService userDetailsService;

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            super.configure(clients);
            clients.inMemory()
                    .withClient("client_1")
                    .secret(passwordEncoder.encode("secret_1"))
                    .scopes("select").authorities("aut")
                    .authorizedGrantTypes("authorization_code", "client_credentials", "refresh_token", "password");
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
            security.passwordEncoder(passwordEncoder)
                    //开启表单认证。创建ClientCredentialsTokenEndpointFilter对请求auth/token拦截,并获取client_id和secret进行身份认证
                    .allowFormAuthenticationForClients();
        }

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService);
        }
    }
           

5.2.3、访问oauth/token

2、oauth2授权

和client_credentials相比,grant_type的值改为了password,同时也增加了username和password两个参数。

5.3、refresh_token

2、oauth2授权

和client_credentials相比,grant_type的值改为了refresh_token,同时也增加了refresh_token参数。

继续阅读