簡介
Spring Cloud Security Oauth2(SpringBoot版本的整合過於麻煩)就是將 OAuth 2.0 和 Spring Security 整合在一起,得到一套完整的安全解決方案,可以實現單點登入、令牌中繼、令牌交換等功能。
在 OAuth 2.0中,provider 角色事實上是把授權服務和資源服務分開,有時候它們也可能在同一個應用中,用 Spring Security OAuth 你可以選擇把它們分成兩個應用,當然多個資源服務可以共享同一個授權服務。獲取 token 的請求由 Spring MVC 的控制端點處理,訪問受保護的資源由標準的 Spring Security 請求過濾器處理。
使用
環境搭建
1、引入依賴
<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>
2、Spring Security 配置
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 強雜湊雜湊加密實現
* @return BCryptPasswordEncoder
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 解決 無法直接注入 AuthenticationManager
* @return authenticationManagerBean
* @throws Exception 異常資訊
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
// 禁用csrf
.cors().and().csrf().disable()
// 過濾請求
.authorizeRequests()
// 放行oauth認證介面
.antMatchers("/login.html", "/oauth/**").permitAll()
// 除上面外的所有請求全部需要鑑權認證,使用者登入後可訪問
.anyRequest().authenticated();
}
}
自定義使用者資訊
@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("使用者名稱或密碼錯誤");
}
}
}
配置認證伺服器
@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()
// 客戶端Id
.withClient("client")
// 祕鑰
.secret("123456")
// 配置訪問token的有效期
.accessTokenValiditySeconds(3600)
// 配置重新整理token的有效期
.refreshTokenValiditySeconds(864000)
// 配置redirect_uri,用於授權成功後跳轉
.redirectUris("http://www.baidu.com")
// 配置申請的許可權範圍
.scopes("all")
// 配置grant_type,表示授權型別,authorization_code:授權碼模式,password:密碼模式
.authorizedGrantTypes("authorization_code", "password");
}
}
配置資源伺服器
@Configuration
// 開啟資源伺服器
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
// 攔截所有請求
.authorizeRequests().anyRequest().authenticated().and()
// 配置需要保護的資源路徑
.requestMatchers().antMatchers("/user/**");
}
}
配置受保護資源
@RestController
public class UserController {
@GetMapping("/user/getCurrentUser")
public Object getCurrentUser(Authentication authentication) {
// 返回自定義資源物件
return authentication.getPrincipal();
}
}
測試
授權碼模式
啟動服務後,在瀏覽器訪問該地址進行登入授權,http://localhost:9401/oauth/authorize?response_type=code&client_id=client&redirect_uri=http://www.baidu.com&scope=all&state=normal
輸入賬號面後進行登入操作,示意圖如下:
登入後進行授權操作,示意圖如下:
授權操作之後,瀏覽器會帶著授權碼跳轉到我們指定的路徑(redirect_rul):https://www.baidu.com/code=eTsADY&state=normal ,此地址中 code 的值就是授權碼,使用授權碼請求該地址獲取訪問令牌:http://localhost:9401/oauth/token ,請求時需要攜帶以下引數(可以通過Postman進行偽造)
-
grant_type:authorization_code,授權碼模式
-
code:eTsADY,剛剛獲取到的
-
client_id:client,認證伺服器配置的
-
redirect_url:http://www.baidu.com ,認證後需要跳轉的地址
-
scope:all,許可權範圍
接下來通過在請求頭中新增訪問令牌,訪問需要登入認證的介面進行測試,發現已經可以成功訪問:http://localhost:9401/user/getCurrentUser ,到此,授權碼模式測試完成。
密碼模式
使用密碼請求該地址獲取訪問令牌:http://localhost:9401/oauth/token ,首先配置認證資訊,其次將 grant_type 修改為 password,即可成功獲取到需要認證的介面資料了。