欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OAuth2 入门---采用密码模式搭建demo工程

程序员文章站 2022-06-13 15:42:10
...

最近在学习OAuth2,虽然网上教学例子很多但不少都存在问题给学习带来很大困扰,因今天拿出时间整理一下思路以密码模式为例写一篇记录学习的文章,目的是希望通过一个简单的demo,帮助有需要的同学入门。本文以实践为主默认读者对OAuth2在概念上有一定了解。

一、OAuth2 密码模式

OAuth2包括授权码、简化、密码客户端四种模式,其中密码模式比较适用于公司内部项目使用,将若干受信系统认证和授权功能抽象为独立的认证服务,可以实现统一用户和单点登录。另外,在OAuth2中又将参与者划分为客户端、资源持有者、资源服务器认证服务器四类角色。下图为密码模式下登录获取资源的授权访问逻辑。

 

OAuth2 入门---采用密码模式搭建demo工程

  1. 客户端(Client)向认证服务器(Authorization)发起登录请求。
  2. 认证服务器(Authorization)向客户端(Client)返回Token。
  3. 客户端(Client)向资源服务器(Resource)发起资源申请。
  4. 资源服务器(Resource)向认证服务器(Authorization)发起Token验证请求。
  5. 认证服务器(Authorization)向资源服务器(Resource)返回Token验证结果。
  6. 资源服务器(Resource)向客户端(Client)返回申请资源。

二、搭建demo工程

工程基于springboot搭建,为了更简便直观权限配置采用内存模式,且仅实现了登录获取资源的最简代码。以下贴出搭建工程的核心类代码。

2.1.搭建认证服务器

pom.xml添加Maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

OAuth2配置

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter {

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

    /**
     * 配置authenticationManager用于认证的过程
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                .authenticationManager(authenticationManager);
    }

    /**
     * 重写此方法用于声明认证服务器能认证的客户端信息
     * 相当于在认证服务器中注册哪些客户端(包括资源服务器)能访问
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory() // 启用内存配置
                .withClient("client")  //声明访问认证服务器的客户端
                .secret(passwordEncoder.encode("123456"))  //客户端访问认证服务器需要带上的密码
                .scopes("read","write")  //获取token包含的哪些权限
                .accessTokenValiditySeconds(3600)  //token过期时间
                .resourceIds("client","resource")  //指明请求的资源服务器
                .authorizedGrantTypes("password")  //密码模式
                .and()
                /**
                 * 资源服务器拿到了客户端请求过来的token之后会
                 * 请求认证服务器去判断此token是否正确或者过期
                 * 所以此时的资源服务器对于认证服务器来说也充当
                 * 了客户端的角色
                 */
                .withClient("resource")
                .secret(passwordEncoder.encode("123456"))
                .scopes("read")
                .accessTokenValiditySeconds(3600)
                .resourceIds("client","resource")
                .authorizedGrantTypes("password");
    }


    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.checkTokenAccess("isAuthenticated()");
    }

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

    @Autowired
    private UserDetailServiceImpl userDetailsService;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(passwordEncoder);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}
@Component
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private PasswordEncoder passwordEncoder;

    //认证的过程,由AuthenticationManager去调,从数据库中查找用户信息

    /**
     * 模拟查询
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return User.withUsername(username)
                .password(passwordEncoder.encode("123456"))
                .authorities("ROLE_ADMIN")
                .build();
    }
}

2.1.搭建认证服务器

pom.xml添加Maven依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        //声明该资源服务器的id,当请求过来时会首先判断token是否有访问该资源服务器的权限
        resources.resourceId("resource").stateless(true);
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                //访问post请求的接口必须要有write权限
                .antMatchers(HttpMethod.POST).access("#oauth2.hasScope('write')")
                //访问get请求的接口必须要有read权限
                .antMatchers(HttpMethod.GET).access("#oauth2.hasScope('read')");
    }

    /**
     * 配置身份认证管理器
     * @return
     * @throws Exception
     */
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        OAuth2AuthenticationManager oAuth2AuthenticationManager = new OAuth2AuthenticationManager();
        oAuth2AuthenticationManager.setTokenServices(tokenServices());
        return oAuth2AuthenticationManager;
    }

    /**
     * 获取权限鉴别对象
     * @return
     */
    @Bean
    public ResourceServerTokenServices tokenServices() {
        RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
        //认证时资源服务器就相当于客户端,需要向认证服务器声明自己的信息是否匹配
        remoteTokenServices.setClientId("resource");
        remoteTokenServices.setClientSecret("123456");
        remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:8080/oauth/check_token");
        return remoteTokenServices;
    }
}
@RestController
@RequestMapping("resource")
public class Resource1Controller {

    @GetMapping
    public String getInfo() {
        return "success";
    }
}

三、测试

OAuth2提供的默认端点(endpoints)

  • /oauth/authorize:授权端点
  • /oauth/token:令牌端点
  • /oauth/confirm_access:用户确认授权提交端点
  • /oauth/error:授权服务错误信息端点
  • /oauth/check_token:用于资源服务访问的令牌解析端点
  • /oauth/token_key:提供公有密匙的端点,如果使用JWT令牌的话

工程搭建好后就可以启动测试了,我使用postman工具来进行测试。

 

OAuth2 入门---采用密码模式搭建demo工程

OAuth2 入门---采用密码模式搭建demo工程

OAuth2 入门---采用密码模式搭建demo工程

3.2. 访问资源服务器

OAuth2 入门---采用密码模式搭建demo工程

OAuth2 入门---采用密码模式搭建demo工程

demo工程可以在码云下载:https://gitee.com/microapps/OAuth2.git 

相关标签: oauth2.0