oauth2.0系統學習3-簡單搭建spring sercurity+oauth2.0的框架

單身貴族男發表於2020-10-30

三、簡單搭建spring sercurity+oauth2.0 的框架

  1. oauth_parent:父工程
  2. oauth-authorizationServer:認證伺服器
  3. 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授權伺服器的開啟

  1. @EnableAuthorizationServer註解
  2. 繼承AuthorizationServerConfigurerAdapter

在這裡插入圖片描述

L2. AuthorizationServerConfigurerAdapter:

  1. 要配置以下幾個類,通過重寫以下方法來實現

    在這裡插入圖片描述

L3. ClientDetailsServiceConfigurer

  1. 用來配置客戶端詳情服務(ClientDetailsService )
    1. ClientDetailsService負責查詢ClientDetails
  2. 可通過記憶體或者資料庫來配置
  3. 客戶端詳情服務
    1. 第三方伺服器來授權伺服器申請訪問資源。通過授權後,頒發clientId,secret給第三方伺服器,表示第三方伺服器是信任的伺服器。

ClientDetails重要屬性列表:

  1. clientId:標識客戶的Id

  2. secret:客戶端祕鑰

  3. scope:限制客戶端的訪問範圍

    1. 預設空:擁有全部的訪問範圍。
  4. authorizedGrantTypes:此客戶端可以使用的授權型別

    1. 預設為空
  5. authorities:客戶端可以使用的許可權

    1. 基於SpringSecurityauthorities

ClientDetails介紹

  1. 能夠在應用程式執行的時候進行更新
  2. 可以通過訪問底層的儲存服務
    1. 客戶端詳情儲存在一個關聯式資料庫的表中
    2. 實現方式
      1. 使用JdbcClientDetailsService
      2. 自己實現ClientRegistrationService介面
      3. 自己實現ClientDetailsService介面

具體程式碼

在這裡插入圖片描述

L4. AuthorizationServerTokenServices

  1. 令牌管理服務
  2. 定義管理令牌的操作
  3. 令牌
    1. 用來載入身份資訊
    2. 包含了這個令牌的相關許可權。
  4. 這個介面的實現,則需要繼承DefaultTokenServices

在這裡插入圖片描述

DefaultTokenServices

  1. 可以修改令牌的格式
  2. 可以修改令牌的儲存。
  3. 預設的,當它嘗試建立一個令牌使用隨機值來進行填充的,除了持久化令牌是委託一個TokenStore介面來實現以外,這個類幾乎幫你做了所有的事情。

TokenStore

  1. 預設實現:InMemoryTokenStore

    1. 所有的令牌是被儲存在了記憶體中
    2. 它可以完美的工作在單伺服器上(即訪問併發量壓力不大的情況下,並且它在失敗的時候不會進行備份)
  2. JdbcTokenStore

    1. 基於JDBC的實現版本,令牌會被儲存進關係型資料庫
    2. 注意點:需要把"spring-jdbc"這個依賴加入到你的classpath當中。
  3. JwtTokenStore

    1. 它可以把令牌相關的資料進行編碼(因此對於後端服務來說,它不需要進行儲存,這將是一個重大優勢)
    2. 缺點:
      1. 撤銷一個已經授權令牌非常困難,所以它通常用來處理一個生命週期較短的令牌以及撤銷重新整理令牌(refresh_token)
      2. 這個令牌佔用的空間會比較大,如果你加入了比較多使用者憑證資訊。JwtTokenStore不會儲存任何資料,但是它在轉換令牌值以及授權資訊方面與DefaultTokenServices所扮演的角色是一樣的。

在這裡插入圖片描述

L5. AuthorizationServerEndpointsConfigurer

  1. 可以完成令牌服務
  2. 可以完成令牌endpoint配置

配置授權型別(GrantTypes)

  1. authenticationManager:認證管理器
    1. 選擇密碼(password)授權型別的時候,請設定這個屬性注入一個AuthenticationManager物件。
  2. userDetailsService:
    1. 設定UserDetailsService介面的實現類(使用者登入驗證)
    2. 或者你可以把這個東西設定到全域性域上面去(例如GlobalAuthenticationManagerConfigurer這個配置物件),當你設定了這個之後,那麼"refresh_token"即重新整理令牌授權型別模式的流程中就會包含一個檢查,用來確保這個賬號是否仍然有效。
  3. authorizationCodeServices:設定授權碼服務
    1. 用於"authorization_code"授權碼型別模式
  4. implicitGrantService:設定隱式授權模式,用來管理隱式授權模式的狀態。
  5. tokenGranter:
    1. 設定TokenGranter介面實現,授權將會交由你來完全掌控,並且會忽略掉上面的這幾個屬性
    2. 這個屬性一般是用作擴充用途的,即標準的四種授權模式已經滿足不了你的需求的時候,才會考慮使用這個。

具體程式碼

在這裡插入圖片描述

配置授權端點的URL(EndpointURLs)

  1. AuthorizationServerEndpointsConfigurer有一個pathMapping()的方法用來配置端點URL連結
  2. 它有兩個引數
    1. 第一個引數:String型別的,這個端點URL的預設連結。
    2. 第二個引數:String型別的,你要進行替代的URL連結。
    3. 以上的引數都將以"/"字元為開始的字串,
    4. 框架的預設URL連結如下列表,可以作為這個pathMapping()方法的第一個引數
      1. /oauth/authorize:授權端點。
      2. /oauth/token:令牌端點。
      3. /oauth/confirm_access:使用者確認授權提交端點。
      4. /oauth/error:授權服務錯誤資訊端點。
      5. /oauth/check_token:用於資源服務訪問的令牌解析端點。
      6. /oauth/token_key:提供公有密匙的端點,如果你使用JWT令牌的話。需要注意的是授權端點這個URL應該被SpringSecurity保護起來只供授權使用者訪問。

L7. authorizationCodeServices

  1. 授權碼服務的儲存方式

具體程式碼

在這裡插入圖片描述


L8. AuthorizationServerSecurityConfigurer

  1. 令牌端點的安全約束

  2. tokenkey:/oauth/token_key端點完全公開。

  3. checkToken:/oauth/check_token端點完全公開。

  4. 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

相關文章