创建一个Spring Security OAuth认证服务

时间:2022-05-06
本文章向大家介绍创建一个Spring Security OAuth认证服务,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

理论

OAuth2是允许应用程序获取对HTTP服务(如GitHub、使用qq登录某网站、使用微信登录某网站等等)上的用户帐户的有限访问权限的授权框架。 它通过将用户身份验证委托给托管用户帐户的服务,并授权第三方应用程序访问用户帐户。 OAuth2为Web和桌面应用程序以及移动设备提供了授权流程。

一、什么是OAuth协议

OAuth(开放授权)是一个开放标准。允许第三方网站在用户授权的前提下访问在用户在服务商那里存储的各种信息。

而这种授权无需将用户提供用户名和密码提供给该第三方网站。

OAuth允许用户提供一个令牌给第三方网站,一个令牌对应一个特定的第三方网站,同时该令牌只能在特定的时间内访问特定的资源。

二、OAuth的原理和授权流程

OAuth的认证和授权的过程中涉及的三方包括:

服务商:

用户使用服务的提供方,一般用来存消息、储照片、视频、联系人、文件等(比如Twitter、Sina等)。

用 户:

服务商的用户

第三方:

通常是网站,该网站想要访问用户存储在服务商那里的信息。比如某个提供照片打印服务的网站,用户想在那里打印自己存在服务商那里的网络相册。在认证过程之前,第三方需要先向服务商申请第三方服务的唯一标识。

三、实例案例

基本上现在的互联网站都会提供开放授权。

以及公司内部的boss系统 统一账号登录 等等,都是类似的open auth标准。

四、OAuth认证和授权的过程

  1. 用户访问第三方网站网站,想对用户存放在服务商的某些资源进行操作。
  2. 第三方网站向服务商请求一个临时令牌。
  3. 服务商验证第三方网站的身份后,授予一个临时令牌。
  4. 第三方网站获得临时令牌后,将用户导向至服务商的授权页面请求用户授权,然后这个过程中将临时令牌和第三方网站的返回地址发送给服务商。
  5. 用户在服务商的授权页面上输入自己的用户名和密码,授权第三方网站访问所想要和能够访问的资源。
  6. 授权成功后,服务商将用户导向第三方网站的返回地址。
  7. 第三方网站根据临时令牌从服务商那里获取访问令牌。
  8. 服务商根据令牌和用户的授权情况授予第三方网站访问令牌。
  9. 第三方网站使用获取到的访问令牌访问存放在服务商对应的用户资源。

实战

一、代码

好,不说理论了,现在我们开始使用spring cloud oauth2创建一个认证服务吧。

新建项目:

引入依赖:

<dependencies>
   <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-oauth2</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
   </dependency>

   <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
      <scope>runtime</scope>
   </dependency>
   <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
   </dependency>
</dependencies>

Account

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
</dependency>
@Data
@NoArgsConstructor
@AllArgsConstructor
@Entity
class Account {

   public Account(String username, String password, boolean active) {
      this.username = username;
      this.password = password;
      this.active = active;
   }

   @Id
   @GeneratedValue
   private Long id;
   private String username, password;
   private boolean active;
}

AccountRepository

interface AccountRepository extends JpaRepository<Account, Long> {

   Optional<Account> findByUsername(String username);
}

AccountUserDetailsService

@Service
class AccountUserDetailsService implements UserDetailsService {

   private final AccountRepository accountRepository;

   public AccountUserDetailsService(AccountRepository accountRepository) {
      this.accountRepository = accountRepository;
   }

   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      return accountRepository.findByUsername(username)
            .map(account -> new User(account.getUsername(),
                  account.getPassword(), account.isActive(), account.isActive(), account.isActive(), account.isActive(),
                  AuthorityUtils.createAuthorityList("ROLE_ADMIN", "ROLE_USER")
            ))
            .orElseThrow(() -> new UsernameNotFoundException("couldn't find the username " + username + "!"));
   }
}

AuthServiceConfiguration

@Configuration
@EnableAuthorizationServer
class AuthServiceConfiguration extends AuthorizationServerConfigurerAdapter {

   private final AuthenticationManager authenticationManager ;

   AuthServiceConfiguration(AuthenticationManager authenticationManager) {
      this.authenticationManager = authenticationManager;
   }

   @Override
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients
            .inMemory()
            .withClient("html5")
            .secret("password")
            .authorizedGrantTypes("password")
            .scopes("openid");
   }

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

AuthServiceApplication

@SpringBootApplication
@RestController
public class AuthServiceApplication {
   @Bean
   CommandLineRunner demo(AccountRepository accountRepository) {
      return args ->
            Stream.of("hezhuofan,spring", "zhangsan,cloud", "lisi,boot", "wangmazi,security")
                  .map(tpl -> tpl.split(","))
                  .forEach(tpl -> accountRepository.save(new Account(tpl[0], tpl[1], true)));
   }

   public static void main(String[] args) {
      SpringApplication.run(AuthServiceApplication.class, args);
   }
}

application.properties:

spring.application.name=auth-service
server.port=9191
server.contextPath=/uaa
security.sessions=if_required

二、模拟请求

使用Postman来模拟请求:

authorization:

headers:

token:

{
"access_token": "67692d8f-49d5-440e-a332-22353e440a5b",
"token_type": "bearer",
"expires_in": 43199,
"scope": "openid"
}

总结

在微服务中,你可以使用oauth认证流程来保护一些需要保护rest api。在公司内部的统一账户登录认证中,也可以通过oauth的方式为要想要接入登录验证的内部项目提供统一登录入口。在互联网上,你也构建自己oauth认证server向第三方应用提供经过用户授权的用户资料。就写这么多,oauth认证中的概念写得不是很详细,以后有机会再专门涉及。

附:Postman安装指南

Chrome浏览器 > Apps > WebStore > search Postman