Spring Cloud Security:Oauth2使用入門

SpringBoot實戰電商項目mall(20k+star)地址: https://github.com/macrozheng/mall

摘要

Spring Cloud Security 爲構建安全的SpringBoot應用提供了一系列解決方案,結合Oauth2能夠實現單點登陸、令牌中繼、令牌交換等功能,本文將對其結合Oauth2入門使用進行詳細介紹。java

OAuth2 簡介

OAuth 2.0是用於受權的行業標準協議。OAuth 2.0爲簡化客戶端開發提供了特定的受權流,包括Web應用、桌面應用、移動端應用等。git

OAuth2 相關名詞解釋

  • Resource owner(資源擁有者):擁有該資源的最終用戶,他有訪問資源的帳號密碼;
  • Resource server(資源服務器):擁有受保護資源的服務器,若是請求包含正確的訪問令牌,能夠訪問資源;
  • Client(客戶端):訪問資源的客戶端,會使用訪問令牌去獲取資源服務器的資源,能夠是瀏覽器、移動設備或者服務器;
  • Authorization server(認證服務器):用於認證用戶的服務器,若是客戶端認證經過,發放訪問資源服務器的令牌。

四種受權模式

  • Authorization Code(受權碼模式):正宗的OAuth2的受權模式,客戶端先將用戶導向認證服務器,登陸後獲取受權碼,而後進行受權,最後根據受權碼獲取訪問令牌;
  • Implicit(簡化模式):和受權碼模式相比,取消了獲取受權碼的過程,直接獲取訪問令牌;
  • Resource Owner Password Credentials(密碼模式):客戶端直接向用戶獲取用戶名和密碼,以後向認證服務器獲取訪問令牌;
  • Client Credentials(客戶端模式):客戶端直接經過客戶端認證(好比client_id和client_secret)從認證服務器獲取訪問令牌。

兩種經常使用的受權模式

受權碼模式

  • (A)客戶端將用戶導向認證服務器;
  • (B)用戶在認證服務器進行登陸並受權;
  • (C)認證服務器返回受權碼給客戶端;
  • (D)客戶端經過受權碼和跳轉地址向認證服務器獲取訪問令牌;
  • (E)認證服務器發放訪問令牌(有須要帶上刷新令牌)。

密碼模式

  • (A)客戶端從用戶獲取用戶名和密碼;
  • (B)客戶端經過用戶的用戶名和密碼訪問認證服務器;
  • (C)認證服務器返回訪問令牌(有須要帶上刷新令牌)。

Oauth2的使用

建立oauth2-server模塊

這裏咱們建立一個oauth2-server模塊做爲認證服務器來使用。
  • 在pom.xml中添加相關依賴:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 在application.yml中進行配置:
server:
  port: 9401
spring:
  application:
    name: oauth2-service
  • 添加UserService實現UserDetailsService接口,用於加載用戶信息:
/**
 * Created by macro on 2019/9/30.
 */
@Service
public class UserService implements UserDetailsService {
    private List<User> userList;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @PostConstruct
    public void initData() {
        String password = passwordEncoder.encode("123456");
        userList = new ArrayList<>();
        userList.add(new User("macro", password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin")));
        userList.add(new User("andy", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client")));
        userList.add(new User("mark", password, AuthorityUtils.commaSeparatedStringToAuthorityList("client")));
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<User> findUserList = userList.stream().filter(user -> user.getUsername().equals(username)).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(findUserList)) {
            return findUserList.get(0);
        } else {
            throw new UsernameNotFoundException("用戶名或密碼錯誤");
        }
    }
}
  • 添加認證服務器配置,使用@EnableAuthorizationServer註解開啓:
/**
 * 認證服務器配置
 * Created by macro on 2019/9/30.
 */
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserService userService;

    /**
     * 使用密碼模式須要配置
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("admin")//配置client_id
                .secret(passwordEncoder.encode("admin123456"))//配置client_secret
                .accessTokenValiditySeconds(3600)//配置訪問token的有效期
                .refreshTokenValiditySeconds(864000)//配置刷新token的有效期
                .redirectUris("http://www.baidu.com")//配置redirect_uri,用於受權成功後跳轉
                .scopes("all")//配置申請的權限範圍
                .authorizedGrantTypes("authorization_code","password");//配置grant_type,表示受權類型
    }
}
  • 添加資源服務器配置,使用@EnableResourceServer註解開啓:
/**
 * 資源服務器配置
 * Created by macro on 2019/9/30.
 */
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .requestMatchers()
                .antMatchers("/user/**");//配置須要保護的資源路徑
    }
}
  • 添加SpringSecurity配置,容許認證相關路徑的訪問及表單登陸:
/**
 * SpringSecurity配置
 * Created by macro on 2019/10/8.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

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

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/oauth/**", "/login/**", "/logout/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .formLogin()
                .permitAll();
    }
}
  • 添加須要登陸的接口用於測試:
/**
 * Created by macro on 2019/9/30.
 */
@RestController
@RequestMapping("/user")
public class UserController {
    @GetMapping("/getCurrentUser")
    public Object getCurrentUser(Authentication authentication) {
        return authentication.getPrincipal();
    }
}

受權碼模式使用

  • 登陸後進行受權操做:

  • 以後會瀏覽器會帶着受權碼跳轉到咱們指定的路徑:
https://www.baidu.com/?code=eTsADY&state=normal
  • 使用受權碼請求該地址獲取訪問令牌:http://localhost:9401/oauth/token
  • 使用Basic認證經過client_id和client_secret構造一個Authorization頭信息;

  • 在body中添加如下參數信息,經過POST請求獲取訪問令牌;

  • 在請求頭中添加訪問令牌,訪問須要登陸認證的接口進行測試,發現已經能夠成功訪問:http://localhost:9401/user/getCurrentUser

密碼模式使用

  • 使用密碼請求該地址獲取訪問令牌:http://localhost:9401/oauth/token
  • 使用Basic認證經過client_id和client_secret構造一個Authorization頭信息;

  • 在body中添加如下參數信息,經過POST請求獲取訪問令牌;

使用到的模塊

springcloud-learning
└── oauth2-server -- oauth2認證測試服務

項目源碼地址

https://github.com/macrozheng/springcloud-learninggithub

公衆號

mall項目全套學習教程連載中,關注公衆號第一時間獲取。web

公衆號圖片

相關文章
相關標籤/搜索