我們見過的很多網站,容許使用第三方賬号登入,他不需要關注使用者資訊,隻需要使用者拿到授權碼就可以通路。
oauth2是用來做三方登入的,他的授權方式有好幾種,授權碼模式、密碼模式、隐式模式、用戶端模式。
oauth2認證的過程如下:一般我們請求一個需要登入的網站A,會提示我們使用第三方網站C的使用者登入,我們登入,這時候需要我們授權,就是authorize,授權之後,會得到一個token,我們拿到這個token就可以通路這個網站A了。A網站不關心C網站的使用者資訊。
springsecurity結合oauth2可以做這個功能,這裡給出一個springboot+security+oauth2的執行個體,這個執行個體很直覺,就是我們有兩個服務A,C,A是需要使用者登入的網站,而C是授權伺服器。當我們通路A的資源:http://localhost:8000/hello,他會跳到C的伺服器上登入,登入完成,授權,就直接調回來A網站,這裡使用了單點登入功能。
建構項目:我這裡建構了一個父級項目securityoauth2,然後加入了兩個子產品:auth-server,client-1。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0zatpFc1cVYsplMMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyAjNxQzMwgTM5EDOwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
項目依賴:
securityoauth2->pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>
</dependencies>
<modules>
<module>auth-server</module>
<module>client-1</module>
</modules>
***********auth-server***************************************
AuthServerConfiguration.java
package com.xxx.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.password.PasswordEncoder;
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.configuration.EnableAuthorizationServer;
@Configuration
@EnableAuthorizationServer
public class AuthServerConfiguration extends AuthorizationServerConfigurerAdapter{
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(passwordEncoder.encode("secret"))
.autoApprove(true)
.redirectUris("http://localhost:8000/login","http://localhost:8001/login")
.scopes("user")
.accessTokenValiditySeconds(7200)
.authorizedGrantTypes("authorization_code");
}
}
授權服務配置這裡,主要設定client_id,以及secret,這裡設定了自動授權autoApprove(true),就是我們輸入使用者名和密碼登入之後,不會出現一個需要我們手動點選authorize的按鈕進行授權。另外配置了redirect_uri,這個是必須的。最後還設定了授權類型,這裡選擇的是授權碼模式。
SecurityConfiguration.java
package com.xxx.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@Order(1)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder builder) throws Exception {
builder.inMemoryAuthentication()
.withUser("admin")
.password(passwordEncoder().encode("admin"))
.roles("admin");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.requestMatchers()
.antMatchers("/login")
.antMatchers("/oauth/authorize")
.and()
.authorizeRequests().anyRequest().authenticated()
.and()
.formLogin()
.and()
.csrf().disable();
}
}
Security是做權限管理的,他需要配置使用者和密碼,這裡采用記憶體儲存的方式,将使用者名和密碼儲存在記憶體中,而不是資料庫或者redis中,關于儲存在哪裡,對于了解oauth2來說,放在記憶體是最簡單的,省去了建表,做資料庫查詢的麻煩。
TestController.java
package com.xxx.web;
import java.security.Principal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/test")
public class TestController {
@GetMapping("/user")
public Principal currentUser(Principal principal) {
return principal;
}
}
這個controller配置,不是用來測試通路他的,而是用來做一個接口,給client-1使用,client-1配置檔案中有個配置:security.oauth2.resource.user-info-uri就是配置的這個接口。
App.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
@EnableResourceServer
public class App {
public static void main( String[] args ){
SpringApplication.run(App.class, args);
}
}
application.yaml
//無配置,可以略
***********client-1*********************************************************
SecurityConfiguration.java
package com.xxx.config;
import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@SuppressWarnings("deprecation")
@Configuration
@EnableOAuth2Sso
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated().and().csrf().disable();
}
}
TestController.java
package com.xxx.web;
import java.util.Arrays;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@GetMapping("/hello")
public String hello() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication.getName()+Arrays.toString(authentication.getAuthorities().toArray());
}
}
App.java
package com.xxx;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class App {
public static void main( String[] args ){
SpringApplication.run(App.class, args);
}
}
application.yaml
server:
port: 8000
servlet:
session:
cookie:
name: s1
security:
oauth2:
client:
client-id: client
client-secret: secret
user-authorization-uri: http://localhost:8080/oauth/authorize
access-token-uri: http://localhost:8080/oauth/token
resource:
user-info-uri: http://localhost:8080/api/test/user
以上都是代碼部分,下面主要是測試,測試過程很簡單,就是我們啟動兩個服務,打開浏覽器通路client-1的http://localhost:8000/hello接口,這時候因為需要登入,會跳轉到auth-server服務的http://localhost:8080/login,登入成功,會接着通路授權接口,授權成功,會跳轉到http://localhost:8000/login,然後跳轉到http://localhost:8000/hello,貌似很複雜,我們先看看效果:
值得注意的是,如圖所示的2、3順序,在這個結果頁面是這樣的,但是在登入頁面并不是這樣,而是先3後2:
可以這麼解釋:client-1的接口http://localhost:8000/hello需要權限才能通路,這時候,需要登入自己的系統http://localhost:8000/login,而自己的系統支援oauth2,他通過封裝了client_id,response_type,redirect_uri,state等參數的授權碼模式請求接口,向auth-server服務發起授權請求http://localhost:8080/oauth/authorize?client_id=client&redirect_uri=http://localhost:8000/login&response_type=code&state=PphcA2,請求會跳轉auth-server的登入頁面 http://localhost:8080/login。
這裡因為使用了單點登入,先從client-1跳轉到了auth-server,最後拿到了code之後,跳回了client-1,通路成功。而頁面上顯示的内容并不是一開始就是這樣子的,他會先跳轉auth-server登入頁面:
以上代碼有了,結果也驗證了,但是個人還是不是很了解這個登入授權的原理和過程,也是在學習中,這個所謂的單點登陸,在代碼上就用了一個@EnableOAuth2Sso注解在client-1項目的SecurityConfiguration類上,該類也是繼承自WebSecurityConfigurerAdapter類,然後覆寫了configure(HttpSecurity http)方法。