數值求和如何屏蔽掉時間
930
2022-05-30
一、OAuth2.0介紹
源碼獲取請加V :boge_java
1.概念說明
先說OAuth,OAuth是Open Authorization的簡寫。
OAuth協議為用戶資源的授權提供了一個安全的、開放而又簡易的標準。與以往的授權方式不同之處是OAuth的授權不會使第三方觸及到用戶的帳號信息(如用戶名與密碼),即第三方無需使用用戶的用戶名與密碼就可以申請獲得該用戶資源的授權,因此OAuth是安全的。
OAuth2.0是OAuth協議的延續版本,但不向前兼容(即完全廢止了OAuth1.0)。
2.使用場景
假設,A網站是一個打印照片的網站,B網站是一個存儲照片的網站,二者原本毫無關聯。如果一個用戶想使用A網站打印自己存儲在B網站的照片,那么A網站就需要使用B網站的照片資源才行。按照傳統的思考模式,我們需要A網站具有登錄B網站的用戶名和密碼才行,但是,現在有了OAuth2,只需要A網站獲取到使用B網站照片資源的一個通行令牌即可!這個令牌無需具備操作B網站所有資源的權限,也無需永久有效,只要滿足A網站打印照片需求即可。這么聽來,是不是有點像單點登錄?NONONO!千萬不要混淆概念!單點登錄是用戶一次登錄,自己可以操作其他關聯的服務資源。OAuth2則是用戶給一個系統授權,可以直接操作其他系統資源的一種方式。但SpringSecurity的OAuth2也是可以實現單點登錄的!
總結一句:SpringSecurity的OAuth2可以做服務之間資源共享,也可以實現單點登錄!
3.OAuth2.0中四種授權方式
為了說明四種模式先準備一張圖
3.1授權碼模式(authorization code)
流程
說明:【A服務客戶端】需要用到【B服務資源服務】中的資源
【A服務客戶端】將用戶自動導航到【B服務認證服務】,這一步用戶需要提供一個回調地址,以備【B服務認證服務】返回授權碼使用。
用戶點擊授權按鈕表示讓【A服務客戶端】使用【B服務資源服務】,這一步需要用戶登錄B服務,也就是說用戶要事先具有B服務的使用權限。
【B服務認證服務】生成授權碼,授權碼將通過第一步提供的回調地址,返回給【A服務客戶端】。
注意這個授權碼并非通行【B服務資源服務】的通行憑證。
【A服務認證服務】攜帶上一步得到的授權碼向【B服務認證服務】發送請求,獲取通行憑證token。
【B服務認證服務】給【A服務認證服務】返回令牌token和更新令牌refresh token。
使用場景授權碼模式是OAuth2中最安全最完善的一種模式,應用場景最廣泛,可以實現服務之間的調用,常見的微信,QQ等第三方登錄也可采用這種方式實現。
3.2簡化模式(implicit)
流程
說明:簡化模式中沒有【A服務認證服務】這一部分,全部有【A服務客戶端】與B服務交互,整個過程不再有授權碼,token直接暴露在瀏覽器。
【A服務客戶端】將用戶自動導航到【B服務認證服務】,這一步用戶需要提供一個回調地址,以備【B服務認證服務】返回token使用,還會攜帶一個【A服務客戶端】的狀態標識state。
用戶點擊授權按鈕表示讓【A服務客戶端】使用【B服務資源服務】,這一步需要用戶登錄B服務,也就是說用戶要事先具有B服務的使用權限。
【B服務認證服務】生成通行令牌token,token將通過第一步提供的回調地址,返回給【A服務客戶端】。
使用場景
適用于A服務沒有服務器的情況。比如:純手機小程序,JavaScript語言實現的網頁插件等。
3.3密碼模式(resource owner password credentials)
流程
直接告訴【A服務客戶端】自己的【B服務認證服務】的用戶名和密碼
【A服務客戶端】攜帶【B服務認證服務】的用戶名和密碼向【B服務認證服務】發起請求獲取token。
【B服務認證服務】給【A服務客戶端】頒發token。
使用場景
此種模式雖然簡單,但是用戶將B服務的用戶名和密碼暴露給了A服務,需要兩個服務信任度非常高才能使用。
3.4客戶端模式(client credentials)
流程
說明:這種模式其實已經不太屬于OAuth2的范疇了。A服務完全脫離用戶,以自己的身份去向B服務索取token。換言之,用戶無需具備B服務的使用權也可以。完全是A服務與B服務內部的交互,與用戶無關了。
A服務向B服務索取token。
B服務返回token給A服務。
使用場景
A服務本身需要B服務資源,與用戶無關。
4.OAuth2.0中表結構說明
說明
如果只是寫個測試案例,完全可以不用連接數據庫,直接將用戶等信息寫在項目中就行。
但是,我們應該把眼光放在企業開發中。試想,我們自己做的一個軟件,想使用微信第三方登錄。難道你還指望微信去修改他們的代碼,讓我們去訪問?想都別想!那么微信會怎么做呢?微信會提供好一個接入的入口,讓我們自己去申請訪問權限。這些數據自然而然需要保存在數據庫中!所以,我們將直接講解數據庫版實現方式!
建表語句
官方SQL地址:
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-securityoauth2/src/test/resources/schema.sql
/* SQLyog Ultimate v12.08 (64 bit) MySQL - 8.0.16 : Database - security_authority ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; /*Table structure for table `oauth_access_token` */ DROP TABLE IF EXISTS `oauth_access_token`; CREATE TABLE `oauth_access_token` ( `token_id` varchar(255) DEFAULT NULL, `token` longblob, `authentication_id` varchar(255) DEFAULT NULL, `user_name` varchar(255) DEFAULT NULL, `client_id` varchar(255) DEFAULT NULL, `authentication` longblob, `refresh_token` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_access_token` */ /*Table structure for table `oauth_approvals` */ DROP TABLE IF EXISTS `oauth_approvals`; CREATE TABLE `oauth_approvals` ( `userId` varchar(255) DEFAULT NULL, `clientId` varchar(255) DEFAULT NULL, `scope` varchar(255) DEFAULT NULL, `status` varchar(10) DEFAULT NULL, `expiresAt` datetime DEFAULT NULL, `lastModifiedAt` datetime DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_approvals` */ /*Table structure for table `oauth_client_details` */ DROP TABLE IF EXISTS `oauth_client_details`; CREATE TABLE `oauth_client_details` ( `client_id` varchar(255) NOT NULL, `resource_ids` varchar(255) DEFAULT NULL, `client_secret` varchar(255) DEFAULT NULL, `scope` varchar(255) DEFAULT NULL, `authorized_grant_types` varchar(255) DEFAULT NULL, `web_server_redirect_uri` varchar(255) DEFAULT NULL, `authorities` varchar(255) DEFAULT NULL, `access_token_validity` int(11) DEFAULT NULL, `refresh_token_validity` int(11) DEFAULT NULL, `additional_information` varchar(255) DEFAULT NULL, `autoapprove` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_client_details` */ /*Table structure for table `oauth_client_token` */ DROP TABLE IF EXISTS `oauth_client_token`; CREATE TABLE `oauth_client_token` ( `token_id` varchar(255) DEFAULT NULL, `token` longblob, `authentication_id` varchar(255) DEFAULT NULL, `user_name` varchar(255) DEFAULT NULL, `client_id` varchar(255) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_client_token` */ /*Table structure for table `oauth_code` */ DROP TABLE IF EXISTS `oauth_code`; CREATE TABLE `oauth_code` ( `code` varchar(255) DEFAULT NULL, `authentication` varbinary(2550) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_code` */ /*Table structure for table `oauth_refresh_token` */ DROP TABLE IF EXISTS `oauth_refresh_token`; CREATE TABLE `oauth_refresh_token` ( `token_id` varchar(255) DEFAULT NULL, `token` longblob, `authentication` longblob ) ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Data for the table `oauth_refresh_token` */ /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
5.表字段說明
5.1oauth_client_details【核心表】
5.2oauth_client_token
該表用于在客戶端系統中存儲從服務端獲取的token數據, 在spring-oauth-server項目中未使用到. 對oauth_client_token表的主要操作在JdbcClientTokenServices.java類中, 更多的細節請參考該類.
5.3oauth_access_token
5.4oauth_refresh_token
5.5oauth_code
二、OAuth2.0實戰案例
本案例同樣通過maven的聚合工程來實現。
1.創建父工程
設置pom文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
2.創建資源項目
接下來創建我們的資源項目
2.1創建項目
2.2導入依賴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2.3配置文件
server: port: 9002 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/srm username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource main: allow-bean-definition-overriding: true #允許我們自己覆蓋spring放入到IOC容器的對象 mybatis: type-aliases-package: com.dpb.domain mapper-locations: classpath:mapper/*.xml logging: level: com.dpb: debug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2.4啟動類
package com.dpb; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 22:33 */ @SpringBootApplication @MapperScan("com.dpb.mapper") public class OAuthSourceApp { public static void main(String[] args) { SpringApplication.run(OAuthSourceApp.class,args); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2.5控制器
/** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 22:34 */ @RestController public class ProductController { @RequestMapping("/findAll") public String findAll(){ return "產品列表信息..."; } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
因為我們引入了 SpringSecurity,所以我們此時沒法直接方法 findAll方法,啟動服務后訪問如下:
那么如何解決呢?前面我們是采用單點登錄的方式解決了這個問題,那么今天我們把這個資源交給OAuth2來管理,使用通行的token來訪問資源
2.6將訪問資源作為OAuth2的資源來管理
復制前面介紹的JWT中的相關代碼(GitHub地址會提供)
即便是用OAuth2管理資源,也一樣需要認證,這兩個對象還是需要的。
2.7編寫資源管理配置類
package com.dpb.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.config.annotation.web.builders.HttpSecurity; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; /** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 22:47 */ @Configuration @EnableResourceServer public class OAuthSourceConfig extends ResourceServerConfigurerAdapter { @Autowired private DataSource dataSource; /** * 指定token的持久化策略 * InMemoryTokenStore表示將token存儲在內存 * Redis表示將token存儲在redis中 * JdbcTokenStore存儲在數據庫中 * @return */ @Bean public TokenStore jdbcTokenStore(){ return new JdbcTokenStore(dataSource); } /** * 指定當前資源的id和存儲方案 * @param resources * @throws Exception */ @Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("product_api").tokenStore(jdbcTokenStore()); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //指定不同請求方式訪問資源所需要的權限,一般查詢是read,其余是write。 .antMatchers(HttpMethod.GET, "/**").access("#oauth2.hasScope('read')") .antMatchers(HttpMethod.POST, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.PATCH, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.PUT, "/**").access("#oauth2.hasScope('write')") .antMatchers(HttpMethod.DELETE, "/**").access("#oauth2.hasScope('write')") .and() .headers().addHeaderWriter((request, response) -> { response.addHeader("Access-Control-Allow-Origin", "*");//允許跨域 if (request.getMethod().equals("OPTIONS")) {//如果是跨域的預檢請求,則原封不動向下傳達請求頭信息 response.setHeader("Access-Control-Allow-Methods", request.getHeader("Access-Control-Request-Method")); response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers")); } }); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
3.創建認證項目
接下來我們創建認證相關的項目
3.1創建項目
3.2導入依賴
和source項目的一樣
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
3.3配置文件
server: port: 9001 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://localhost:3306/srm username: root password: 123456 type: com.alibaba.druid.pool.DruidDataSource main: allow-bean-definition-overriding: true #允許我們自己覆蓋spring放入到IOC容器的對象 mybatis: type-aliases-package: com.dpb.domain mapper-locations: classpath:mapper/*.xml logging: level: com.dpb: debug
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
3.4啟動類
/** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 23:06 */ @SpringBootApplication @MapperScan("com.dpb.mapper") public class OAuthServerApp { public static void main(String[] args) { SpringApplication.run(OAuthServerApp.class,args); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
3.5復制之前認證的代碼
3.6提供SpringSecurity的配置類
package com.dpb.config; import com.dpb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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; /** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 23:09 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserService userService; @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userService).passwordEncoder(passwordEncoder()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login") .permitAll() .and() .csrf() .disable(); } //AuthenticationManager對象在OAuth2認證服務中要使用,提前放入IOC容器中 @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
3.7提供OAuth2的配置類
package com.dpb.config; import com.dpb.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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.approval.ApprovalStore; import org.springframework.security.oauth2.provider.approval.JdbcApprovalStore; import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore; import javax.sql.DataSource; /** * @program: springboot-security-oauth2-demo * @description: * @author: 波波烤鴨 * @create: 2019-12-04 23:12 */ @Configuration @EnableAuthorizationServer public class OauthServerConfig extends AuthorizationServerConfigurerAdapter { //數據庫連接池對象 @Autowired private DataSource dataSource; //認證業務對象 @Autowired private UserService userService; //授權模式專用對象 @Autowired private AuthenticationManager authenticationManager; //客戶端信息來源 @Bean public JdbcClientDetailsService jdbcClientDetailsService(){ return new JdbcClientDetailsService(dataSource); } //token保存策略 @Bean public TokenStore tokenStore(){ return new JdbcTokenStore(dataSource); } //授權信息保存策略 @Bean public ApprovalStore approvalStore(){ return new JdbcApprovalStore(dataSource); } //授權碼模式數據來源 @Bean public AuthorizationCodeServices authorizationCodeServices(){ return new JdbcAuthorizationCodeServices(dataSource); } //指定客戶端信息的數據庫來源 @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(jdbcClientDetailsService()); } //檢查token的策略 @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); security.checkTokenAccess("isAuthenticated()"); } //OAuth2的主配置信息 @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .approvalStore(approvalStore()) .authenticationManager(authenticationManager) .authorizationCodeServices(authorizationCodeServices()) .tokenStore(tokenStore()); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
4.測試
4.1在數據庫中手動添加客戶端信息
所有要使用當前項目資源的項目,都是我們的客戶端。比如我們之前舉的例子,A服務打印照片,B服務存儲照片。A服務要使用B服務的資源,那么A服務就是B服務的客戶端。這里要區分用戶的信息和客戶端信息,用戶信息是用戶在B服務上注冊的用戶信息,在sys_user表中。客戶端信息是A服務在B服務中注冊的賬號,在OAuth2的oauth_client_details表中。
測試數據sql語句如下:
INSERT INTO `oauth_client_details` ( `client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove` ) VALUES ( 'bobo_one', 'product_api', '$2a$10$CYX9OMv0yO8wR8rE19N2fOaXDJondci5uR68k2eQJm50q8ESsDMlC', 'read, write', 'client_credentials,implicit,authorization_code,refresh_token,password', 'http://www.baidu.com', NULL, NULL, NULL, NULL, 'false' );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
這里注意resource_ids不要寫錯,回調地址web_server_redirect_uri先寫成百度。
啟動兩個服務測試
4.2 授權碼模式測試
在地址欄訪問地址
http://localhost:9001/oauth/authorize?response_type=code&client_id=bobo_one
跳轉到SpringSecurity默認認證頁面,提示用戶登錄個人賬戶【這里是sys_user表中的數據】
登錄成功后詢問用戶是否給予操作資源的權限,具體給什么權限。Approve是授權,Deny是拒絕。這里我們選擇read和write都給予Approve
點擊Authorize后跳轉到回調地址并獲取授權碼
使用授權碼到服務器申請通行令牌token(測試使用的是PostMan)
重啟資源服務器,然后攜帶通行令牌再次去訪問資源服務器,大功告成!
4.3簡化模式測試
在地址欄訪問地址
http://localhost:9001/oauth/authorize?response_type=token&client_id=bobo_one
由于上面用戶已經登錄過了,所以無需再次登錄,其實和上面是有登錄步驟的,這時,瀏覽器直接返回了token
直接訪問資源服務器
4.4密碼模式測試
申請token
4.5客戶端模式測試
申請token
源碼獲取請加V :boge_java
Access 網站
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。