Spring 2.0.4中使用OAuth2.0认证

♀尐吖头ヾ 提交于 2020-04-13 17:07:48

【今日推荐】:为什么一到面试就懵逼!>>>

问题

最近有个需求是要给系统里面的所有REST请求,弄一个token,然后,那着这个访问token,去掉接口。阮一峰写了两遍文章,值得我们一看:

这里假设我们已经实现spring的rest api了。

Maven

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
...
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security.oauth.boot</groupId>
    <artifactId>spring-security-oauth2-autoconfigure</artifactId>
    <version>2.0.4.RELEASE</version>
</dependency>
...

application.properties

security.oauth2.client.client-id=client
security.oauth2.client.client-secret=secret
security.oauth2.client.authorized-grant-types=password,authorization_code,refresh_token,implicit
security.oauth2.client.scope=read,write,trust
security.oauth2.client.access-token-validity-seconds=120
security.oauth2.client.refresh-token-validity-seconds=600

#超级管理员
admin.username=admin
amdin.password=pwd

Java

Application.Java

package com.zyl;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@SpringBootApplication
// 启用认证服务器
@EnableAuthorizationServer
// 启用资源服务器
@EnableResourceServer
public class Application {

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

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        // 添加密码编码器
        return new BCryptPasswordEncoder();
    }

    @Bean
    public TokenStore tokenStore() {
        // 添加内存保存token 
        return new InMemoryTokenStore();
    }
}

这里是单模块的工程,没有把认证服务和资源服务进行分离处理。这里主要就是启用认证服务器和资源服务器,还添加密码编码器和token内存保存器,密码编码和token内存保存,是后面待使用。

Oauth2SecurityConfig.Java

package com.zyl.config;

import com.zyl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@EnableWebSecurity
public class Oauth2SecurityConfig extends WebSecurityConfigurerAdapter {

    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    private final UserService userService;

    @Autowired
    public Oauth2SecurityConfig(
            BCryptPasswordEncoder bCryptPasswordEncoder, UserService userService) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
        this.userService = userService;
    }

    // 配置这个bean会在做AuthorizationServerConfigurer配置的时候使用
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

    // @Override
    // public void configure(WebSecurity web) throws Exception {
    //     // TODO 关闭spring security
    //     web.ignoring().antMatchers("/**");
    // }
}

这里主要就是配置@EnableWebSecurity,启用Spring Security,还有就是实例化了AuthenticationManager认证管理器,以及注入了userDetailsService用户服务和BCryptPasswordEncoder密码编码器,给认证的过程中配置了一个用户服务和密码编码器。这里的UserService是对UserDetailsService接口,后面我们再讨论这个问题。

AuthorizationServerConfiguration.Java

package com.zyl.config;

import com.zyl.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Component;

@Component
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {


    private final TokenStore tokenStore;

    private final AuthenticationManager authenticationManager;

    private final UserService userService;

    private final BCryptPasswordEncoder bCryptPasswordEncoder;

    // 从资源文件中读取http基本认证username
    @Value("${security.oauth2.client.client-id:client}")
    private String client;

    // 从资源文件中读取oauth2的授权类型
    @Value("${security.oauth2.client.authorized-grant-types:password,authorization_code,refresh_token,implicit}")
    private String[] grantTypes;

    // 从资源文件中读取http基本认证password
    @Value("${security.oauth2.client.client-secret:pwd}")
    private String secret;

    // 从资源文件中读取oauth2的权限范围
    @Value("${security.oauth2.client.scope:read,write,trust}")
    private String scopes;

    /**
     * security.oauth2.client.access-token-validity-seconds=120
     * Access token is only valid for 2 minutes.
     */
    @Value("${security.oauth2.client.access-token-validity-seconds:120}")
    private int accessTokenValiditySeconds;

    /**
     * security.oauth2.client.refresh-token-validity-seconds=600
     * Refresh token is only valid for 10 minutes.;
     */
    @Value("${security.oauth2.client.refresh-token-validity-seconds:600}")
    private int refreshTokenValiditySeconds;

    @Autowired
    public AuthorizationServerConfiguration(TokenStore tokenStore, AuthenticationManager authenticationManager, UserService userService, BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.tokenStore = tokenStore;
        this.authenticationManager = authenticationManager;
        this.userService = userService;
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

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

    @Override
    public void configure(
            ClientDetailsServiceConfigurer clients
    ) throws Exception {
        clients.inMemory()
                .withClient(client)
                .authorizedGrantTypes(grantTypes)
                .secret(bCryptPasswordEncoder.encode(secret))
                .scopes(scopes)
                .accessTokenValiditySeconds(accessTokenValiditySeconds)
                .refreshTokenValiditySeconds(refreshTokenValiditySeconds);
    }

}

这里主要是对OAuth2.0认证的设置,以及认证的token保存和认证管理器的配置,已经自定义的用户认证服务userService

UserService.Java

package com.zyl.service;

import com.zyl.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Optional;

@Service
public class UserService implements UserDetailsService {

  // 注入密码编码器
  private final BCryptPasswordEncoder bCryptPasswordEncoder;

  // 从资源文件中读取超级管理员用户名
  @Value("${admin.username:zyl}")
  private String adminUsername;

  // 从资源文件中读取超级管理员密码
  @Value("${amdin.password:pwd}")
  private String adminPassword;

  @Autowired
  public UserService(
      BCryptPasswordEncoder bCryptPasswordEncoder
  ) {
    this.bCryptPasswordEncoder = bCryptPasswordEncoder;
  }

  @Override
  public UserDetails loadUserByUsername(String username) {
    // 查找用户
    User user = findByUid(username);
    ...
    org.springframework.security.core.userdetails.User.UserBuilder builder = org.springframework.security.core.userdetails.User.withUsername(username);
    if (user != null){
      builder.password(user.getPassword());
	  builder.roles("USER");
      return builder.build();
    } else {
      // 超级管理员
      if (username.equals(adminUsername)){
        builder.password(bCryptPasswordEncoder.encode(adminPassword));
        builder.roles("ADMIN");
        return builder.build();
      }
      // TODO 没找用户处理
      ...
    }
  }
}

这里主要从系统中查找到对应的用户,并将找到用户交给Spring进行认证判断。

User.Java

package com.zyl.model;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    /**
     * 用户uid
     */
    private String uid;
    /**
     * 用户名
     */
    private String username;

    /**
     * 密码
     */
    private String password;

   ...
}

这个是一个简单的java bean。

PostMan

接下来,通过postman做三件事情:

  • 通过认证,获得用户访问token和刷新token
  • 通过访问token调用api
  • 访问token到期失效后,刷新token

授权认证

获得访问token第一步

根据资源文件中配置:

security.oauth2.client.client-id=client
security.oauth2.client.client-secret=secret

设置http基本认证的用户名和密码。 访问token

获取访问token 这样就完成OAuth的访问token的获取。

{
    "access_token": "a5a67cf1-70df-450c-8241-8409abe988e2",
    "token_type": "bearer",
    "refresh_token": "56675215-e22a-4bff-8489-fbc3a28699de",
    "expires_in": 119,
    "scope": "read,write,trust"
}

调用API

调用API 获得到访问Token后,需要调用API,只需要在请求头中添加Authorization即可:

Authorization: Bearer ...

刷新Token

如果访问Token过期了,还需要刷新该授权: 刷新token 注意,这仍然需要配置http基本认证的用户和密码,才能够进行post刷新token的。

{
    "access_token": "edb39ddc-1257-4e53-96c0-66232a8d158e",
    "token_type": "bearer",
    "refresh_token": "56675215-e22a-4bff-8489-fbc3a28699de",
    "expires_in": 119,
    "scope": "read,write,trust"
}

总结

好,这样就给我们的REST接口加上了OAuth2.0的认证服务。

参考:

demo-oauth2-spring-security

Secure Spring REST API using OAuth2

Spring REST API + OAuth2 + AngularJS

OAuth2 Autoconfig

理解OAuth 2.0

OAuth 2.0 Grant Types

讲真,别再使用JWT了

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!