oauth2.0系統學習3-簡單搭建spring sercurity+oauth2.0的框架
三、簡單搭建spring sercurity+oauth2.0 的框架
- oauth_parent:父工程
- oauth-authorizationServer:認證伺服器
- oauth-ResourceServer:資源伺服器
oauth_parent:父工程程式碼(就是pom配置)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>oauth-learn</groupId>
<artifactId>oauth-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.interceptor</groupId>
<artifactId>javax.interceptor-api</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.47</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.0.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>utf-8</encoding>
<useDefaultDelimiters>true</useDefaultDelimiters>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.1 、搭建一個認證伺服器 oauth-authorizationServer
001)、程式碼
package com.feizhou.oauth.bean;
import lombok.Data;
/**
* 描述該類- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Data
public class UserAuthorizeBean {
private Long id;
private Long userId;
private String authorizeCode;
}
package com.feizhou.oauth.bean;
import lombok.Data;
/**
* 描述該類- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Data
public class UserBean {
private Long id;
private String userName;
private String password;
}
package com.feizhou.oauth.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
/**
* 描述該類- JPA
*
* @author zhoufei
* @class: AuthorizationServer
* @date 2020/10/28 21:26
* @Verson 1.0 -2020/10/28 21:26
* @see
*/
@Configuration
@EnableAuthorizationServer // 配置授權服務。
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {
// 用來配置客戶端詳情服務
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 這裡是第三方合作使用者的客戶id,祕鑰的配置
// 使用in-memory儲存
clients.inMemory()
// client_id,使用者賬號
.withClient("c1")
// 客戶端金鑰
.secret(new BCryptPasswordEncoder().encode("secret"))
// 資源列表,資源標識
.resourceIds("res1")
// 授權型別(4種)
.authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit",
"refresh_token")
// 客戶端允許的授權範圍
.scopes("all")
// false跳轉到授權頁面,讓使用者點選授權,如果是true,相當於自動點選授權,就不跳轉授權頁面
.autoApprove(false)//
// 加上驗證回撥地址,返回授權碼資訊
.redirectUris("http://www.baidu.com");
// 如果有多個使用者,配置多個客戶詳情
// .and().withClient()
}
@Autowired
// 令牌儲存策略
private TokenStore tokenStore;
@Autowired
// 客戶端詳情服務,也就是configure(ClientDetailsServiceConfigurer clients)方法
private ClientDetailsService clientDetailsService;
// 令牌管理服務
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service = new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
service.setSupportRefreshToken(true);// 支援重新整理
service.setTokenStore(tokenStore);// 令牌儲存
service.setAccessTokenValiditySeconds(7200); // 令牌預設有效期2小時
service.setRefreshTokenValiditySeconds(259200); // 重新整理令牌預設有效期3天
return service;
}
@Autowired
// 授權碼服務
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
// 認證管理
private AuthenticationManager authenticationManager;
@Autowired
// 令牌管理服務
private AuthorizationServerTokenServices authorizationServerTokenServices;
@Override
// 用來配置令牌(token)的訪問端點
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints
// 密碼模式需要
.authenticationManager(authenticationManager)
// 授權碼模式需要
.authorizationCodeServices(authorizationCodeServices)
// 令牌管理服務
.tokenServices(authorizationServerTokenServices).allowedTokenEndpointRequestMethods(HttpMethod.POST);// 允許post提交
}
@Bean
// 授權碼伺服器
public AuthorizationCodeServices authorizationCodeServices() {
// 授權碼模式的授權碼採用記憶體方式儲存
return new InMemoryAuthorizationCodeServices();
}
@Override
// 用來配置令牌端點的安全約束,攔截規則
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
// 提供公有密匙的端點,如果你使用JWT令牌的話, 允許
.tokenKeyAccess("permitAll()")
// oauth/check_token:用於資源服務訪問的令牌解析端點,允許
.checkTokenAccess("permitAll()")
// 表單認證,申請令牌
.allowFormAuthenticationForClients();
}
@Bean
// 令牌儲存策略
public TokenStore tokenStore() {
// 記憶體儲存,普通令牌
return new InMemoryTokenStore();
}
}
package com.feizhou.oauth.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author Administrator
* @version 1.0
**/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
// 認證管理器
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
// 密碼編碼器
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 安全攔截機制
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
csrf().disable().
authorizeRequests().
antMatchers("/admin/p1").
hasAnyAuthority("p1").
antMatchers("/user/p2").
hasAnyAuthority("p2").
antMatchers("/login*")
.permitAll().
anyRequest().
authenticated().
and().
formLogin();
}
}
package com.feizhou.oauth.dao;
import java.util.ArrayList;
import java.util.List;
import com.feizhou.oauth.bean.UserAuthorizeBean;
import com.feizhou.oauth.bean.UserBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
/**
* 描述該類- JPA
*
* @author zhoufei
* @class: UserBean
* @date 2020/10/27 16:09
* @Verson 1.0 -2020/10/27 16:09
* @see
*/
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
// 根據賬號查詢使用者資訊
public UserBean getUserByUsername(String username) {
String sql = "select * from user where user_name = ?";
// 連線資料庫查詢使用者
List<UserBean> list = jdbcTemplate.query(sql, new Object[] { username },
new BeanPropertyRowMapper<>(UserBean.class));
if (list != null && list.size() == 1) {
return list.get(0);
}
return null;
}
// 根據使用者id查詢使用者許可權
public List<String> getAuthorize(Long userId) {
String sql = "SELECT * FROM user_authorize WHERE user_id =?";
List<UserAuthorizeBean> list = jdbcTemplate.query(sql, new Object[] { userId },
new BeanPropertyRowMapper<>(UserAuthorizeBean.class));
List<String> authorizes = new ArrayList<>();
list.forEach(c -> authorizes.add(c.getAuthorizeCode()));
return authorizes;
}
}
package com.feizhou.oauth.service;
import java.util.List;
import com.feizhou.oauth.bean.UserBean;
import com.feizhou.oauth.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
/**
* @author Administrator
* @version 1.0
**/
@Service
public class SpringDataUserDetailsService implements UserDetailsService {
@Autowired
UserDao userDao;
// 根據賬號查詢使用者資訊,
// 通過@Service將SpringDataUserDetailsService注入容器,通過UserDetailsService介面表明該類的型別是UserDetailsService
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 將來連線資料庫根據賬號查詢使用者資訊
UserBean bean = userDao.getUserByUsername(username);
if (bean == null) {
// 如果使用者查不到,返回null,由provider來丟擲異常
return null;
}
// 查詢當前資料庫的使用者資源許可權
List<String> authorize = userDao.getAuthorize(bean.getId());
String[] authorizeArr = new String[authorize.size()];
authorize.toArray(authorizeArr);
// 新增許可權
UserDetails userDetails = User.withUsername(bean.getUserName()).password(bean.getPassword())
.authorities(authorizeArr).build();
return userDetails;
}
}
application.properties
spring.application.name=oauth-authorizationServer
server.port=8081
spring.datasource.url = jdbc:mysql://zhoufei.ali.db.com:3306/test?useUnicode=true
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>oauth-parent</artifactId>
<groupId>oauth-learn</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>oauth-authorizationServer</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
002)、理論,解讀程式碼
L1. OAuth2.0授權伺服器的開啟
- @EnableAuthorizationServer註解
- 繼承AuthorizationServerConfigurerAdapter
L2. AuthorizationServerConfigurerAdapter:
-
要配置以下幾個類,通過重寫以下方法來實現
L3. ClientDetailsServiceConfigurer
- 用來配置客戶端詳情服務(ClientDetailsService )
- ClientDetailsService負責查詢ClientDetails
- 可通過記憶體或者資料庫來配置
- 客戶端詳情服務
- 第三方伺服器來授權伺服器申請訪問資源。通過授權後,頒發clientId,secret給第三方伺服器,表示第三方伺服器是信任的伺服器。
ClientDetails重要屬性列表:
-
clientId:標識客戶的Id
-
secret:客戶端祕鑰
-
scope:限制客戶端的訪問範圍
- 預設空:擁有全部的訪問範圍。
-
authorizedGrantTypes:此客戶端可以使用的授權型別
- 預設為空
-
authorities:客戶端可以使用的許可權
- 基於SpringSecurityauthorities
ClientDetails介紹
- 能夠在應用程式執行的時候進行更新
- 可以通過訪問底層的儲存服務
- 客戶端詳情儲存在一個關聯式資料庫的表中
- 實現方式
- 使用JdbcClientDetailsService
- 自己實現ClientRegistrationService介面
- 自己實現ClientDetailsService介面
具體程式碼
L4. AuthorizationServerTokenServices
- 令牌管理服務
- 定義管理令牌的操作
- 令牌
- 用來載入身份資訊
- 包含了這個令牌的相關許可權。
- 這個介面的實現,則需要繼承DefaultTokenServices
DefaultTokenServices
- 可以修改令牌的格式
- 可以修改令牌的儲存。
- 預設的,當它嘗試建立一個令牌使用隨機值來進行填充的,除了持久化令牌是委託一個TokenStore介面來實現以外,這個類幾乎幫你做了所有的事情。
TokenStore
-
預設實現:InMemoryTokenStore
- 所有的令牌是被儲存在了記憶體中
- 它可以完美的工作在單伺服器上(即訪問併發量壓力不大的情況下,並且它在失敗的時候不會進行備份)
-
JdbcTokenStore
- 基於JDBC的實現版本,令牌會被儲存進關係型資料庫
- 注意點:需要把"spring-jdbc"這個依賴加入到你的classpath當中。
-
JwtTokenStore
- 它可以把令牌相關的資料進行編碼(因此對於後端服務來說,它不需要進行儲存,這將是一個重大優勢)
- 缺點:
- 撤銷一個已經授權令牌非常困難,所以它通常用來處理一個生命週期較短的令牌以及撤銷重新整理令牌(refresh_token)
- 這個令牌佔用的空間會比較大,如果你加入了比較多使用者憑證資訊。JwtTokenStore不會儲存任何資料,但是它在轉換令牌值以及授權資訊方面與DefaultTokenServices所扮演的角色是一樣的。
L5. AuthorizationServerEndpointsConfigurer
- 可以完成令牌服務
- 可以完成令牌endpoint配置
配置授權型別(GrantTypes)
- authenticationManager:認證管理器
- 選擇密碼(password)授權型別的時候,請設定這個屬性注入一個AuthenticationManager物件。
- userDetailsService:
- 設定UserDetailsService介面的實現類(使用者登入驗證)
- 或者你可以把這個東西設定到全域性域上面去(例如GlobalAuthenticationManagerConfigurer這個配置物件),當你設定了這個之後,那麼"refresh_token"即重新整理令牌授權型別模式的流程中就會包含一個檢查,用來確保這個賬號是否仍然有效。
- authorizationCodeServices:設定授權碼服務
- 用於"authorization_code"授權碼型別模式
- implicitGrantService:設定隱式授權模式,用來管理隱式授權模式的狀態。
- tokenGranter:
- 設定TokenGranter介面實現,授權將會交由你來完全掌控,並且會忽略掉上面的這幾個屬性
- 這個屬性一般是用作擴充用途的,即標準的四種授權模式已經滿足不了你的需求的時候,才會考慮使用這個。
具體程式碼
配置授權端點的URL(EndpointURLs)
- AuthorizationServerEndpointsConfigurer有一個pathMapping()的方法用來配置端點URL連結
- 它有兩個引數
- 第一個引數:String型別的,這個端點URL的預設連結。
- 第二個引數:String型別的,你要進行替代的URL連結。
- 以上的引數都將以"/"字元為開始的字串,
- 框架的預設URL連結如下列表,可以作為這個pathMapping()方法的第一個引數
- /oauth/authorize:授權端點。
- /oauth/token:令牌端點。
- /oauth/confirm_access:使用者確認授權提交端點。
- /oauth/error:授權服務錯誤資訊端點。
- /oauth/check_token:用於資源服務訪問的令牌解析端點。
- /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話。需要注意的是授權端點這個URL應該被SpringSecurity保護起來只供授權使用者訪問。
L7. authorizationCodeServices
- 授權碼服務的儲存方式
具體程式碼
L8. AuthorizationServerSecurityConfigurer
-
令牌端點的安全約束
-
tokenkey:/oauth/token_key端點完全公開。
-
checkToken:/oauth/check_token端點完全公開。
-
allowFormAuthenticationForClients:允許表單認證
具體程式碼
3.2 、驗證認證伺服器
001) 、授權碼模式
http://localhost:8081/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com
先讓你登入(Security的原因)
使用者授權
返回授權碼
請求taken
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=kZAlHW&redirect_uri=http://www.baidu.com
002) 、簡化模式
http://localhost:8081/oauth/authorize?client_id=c1&response_type=token&scope=all&redirect_uri=http://www.baidu.com
003) 、密碼模式
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=admin&password=admin
004) 、客戶端模式
http://localhost:8081/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials
程式碼文章
https://gitee.com/DanShenGuiZu/learnDemo/tree/mysql_mybaties_DB/oauth2.0-learn
相關文章
- Gin 框架的簡單搭建框架
- 一個簡簡單單的紅點系統框架框架
- SSM-框架搭建-tank後臺學習系統SSM框架
- 簡易版的Spring框架之IOC簡單實現Spring框架
- OAuth2.0分散式系統環境搭建OAuth分散式
- Spring框架系列(2) - Spring簡單例子引入Spring要點Spring框架單例
- Java學習筆記—開源框架Netty的簡單使用Java筆記框架Netty
- 2020年總結-用學習過的技術搭建一個簡單的微服務框架 + 原始碼微服務框架原始碼
- 理解vue ssr原理,自己搭建簡單的ssr框架Vue框架
- Spring Boot:簡單使用EhCache快取框架Spring Boot快取框架
- Spring Cloud Security OAuth2.0入門: AuthorizationServer搭建(一)SpringCloudOAuthServer
- Spring Boot--日誌框架的學習Spring Boot框架
- YII 初體驗 —— 搭建一個簡單的 Todo List 系統
- Spring框架學習筆記(1)Spring框架筆記
- spring框架學習 依賴注入Spring框架依賴注入
- 搭建OAuth2.0OAuth
- 簡易版的Spring框架之AOP簡單實現(對我來說不簡單啊)Spring框架
- SpringCloud之Sentinel高可用流量框架簡單搭建SpringGCCloud框架
- [Rust]使用Rocket框架搭建簡單Web服務Rust框架Web
- .Net Core Web Api 框架搭建簡單步驟WebAPI框架
- Spring入門學習手冊 1:最簡單的反轉控制Spring
- Flutter學習(7)——網路請求框架Dio簡單使用Flutter框架
- SSM框架學習之Spring的AOP學習以及資料整理SSM框架Spring
- [Python急救站]簡單的學生管理系統Python
- Git簡單學習Git
- 簡單學習jsJS
- 直播系統搭建,簡單實現Android應用的啟動頁Android
- 學習abp vnext框架到精簡到我的Vop框架框架
- Java簡單學生資訊管理系統Java
- Golang學習筆記 – 標準庫”net/http”的簡析及自制簡單路由框架Golang筆記HTTP路由框架
- 學習使用 Goframe 框架搭建後臺GoFrame框架
- 深入學習Spring框架(一)- 入門Spring框架
- Spring框架簡介⑩Spring框架
- Spring框架簡介⑨Spring框架
- Spring框架簡介⑧Spring框架
- Spring框架簡介⑦Spring框架
- Spring框架簡介⑥Spring框架
- Spring框架簡介⑤Spring框架