日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

Spring Security OAuth2.0认证授权

發布時間:2024/1/18 javascript 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Spring Security OAuth2.0认证授权 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1、基本概念
      • 1.1.什么是認證
      • 1.2 什么是會話
      • 1.3什么是授權
      • 1.4授權的數據模型
      • 1.4 RBAC
        • 1.4.1 基于角色的訪問控制
    • 2、基于Session的認證方式
    • 3、整合案例
      • 3.1 SpringMVC+Servlet3.0模擬認證、會話、授權
        • 3.1.1引入依賴
        • 3.1.2 Spring 容器配置ApplicationConfig
        • 3.1.3 servletContext配置 WebConfig
        • 3.1.4 加載 Spring容器SpringApplicationInitializer
        • 3.1.5實現認證功能
        • 3.1.6實現會話功能
        • 3.1.7實現授權功能
      • 3.2 Spring集成Security
      • 3.3 SpringBoot集成Security
    • 4、工作原理
      • 4.1結構總覽
      • 4.2認證流程
        • AuthenticationProvider接口
        • UserDetailsService接口
        • PasswordEncoder接口
      • 4.3授權流程
        • AccessDecisionManager(訪問決策管理器)
        • 授權決策
    • 5、自定認證
      • 5.1登錄
      • 5.2會話
      • 5.3退出
      • 5.4授權
        • web授權
        • 方法授權
    • 6、分布式系統認證方案
      • 6.1什么是分布式系統
      • 6.2 分布式認證需求
        • 統一認證授權
        • 應用接入認證
      • 6.3 分布式認證方案
        • 選型分析
          • 1、基于session的認證方式
          • 2、基于token的認證方式
        • 技術方案
    • 7、OAuth2.0 開放授權
      • 7.1 OAuth2.0介紹
      • 7.2 Spring Cloud Security OAuth2 的實現
        • 7.2.1代碼略。。。。。。
        • 7.2.2授權服務器配置
          • 1)@EnableAuthorizationServer 注解
          • 2)配置客戶端詳細信息
          • 3)管理令牌服務
          • 4)令牌訪問端點配置
          • 5)配置授權端點的URL(Endpoint URLs)
          • 6)令牌端點的安全約束
        • 7.2.3.授權碼模式
        • 7.2.4.簡化模式
        • 7.2.5.密碼模式
        • 7.2.6.客戶端模式
        • 7.2.7.資源服務測試
          • 資源服務器配置
          • 驗證token
      • 7.3 JWT令牌
        • 什么是JWT?
        • JWT令牌結構
    • 8、Spring Security實現分布式系統授權
      • 需求分析
      • 網關

什么是認證、授權、會話。

Java Servlet為支持http會話做了哪些事兒。

基于session認證機制的運作流程。

基于token認證機制的運作流程。

理解Spring Security的工作原理,Spring Security結構總覽,認證流程和授權,中間涉及到哪些組件,這些組件分

別處理什么,如何自定義這些組件滿足個性需求。

OAuth2.0認證的四種模式?它們的大體流程是什么?

Spring cloud Security OAuth2包括哪些組件?職責?

分布式系統認證需要解決的問題?

代碼參考:ahcfl_leon/work-study

1、基本概念

1.1.什么是認證

系統為什么要認證?

認證是為了保護系統的隱私數據與資源,用戶的身份合法方可訪問該系統的資源。

認證 :用戶認證就是判斷一個用戶的身份是否合法的過程,用戶去訪問系統資源時系統要求驗證用戶的身份信 息,身份合法方可繼續訪問,不合法則拒絕訪問。常見的用戶身份認證方式有:用戶名密碼登錄,二維碼登錄,手 機短信登錄,指紋認證等方式。

1.2 什么是會話

用戶認證通過后,為了避免用戶的每次操作都進行認證可將用戶的信息保證在會話中。會話就是系統為了保持當前 用戶的登錄狀態所提供的機制,常見的有基于session方式、基于token方式等。

1)基于session的認證方式如下圖:

它的交互流程是,用戶認證成功后,在服務端生成用戶相關的數據保存在session(當前會話)中,發給客戶端的 sesssion_id 存放到 cookie 中,這樣用戶客戶端請求時帶上 session_id 就可以驗證服務器端是否存在 session 數據,以此完成用戶的合法校驗,當用戶退出系統或session過期銷毀時,客戶端的session_id也就無效了。

2)基于token方式如下圖:

它的交互流程是,用戶認證成功后,服務端生成一個token發給客戶端,客戶端可以放到 cookie 或 localStorage 等存儲中,每次請求時帶上 token,服務端收到token通過驗證后即可確認用戶身份。

總結:

基于session的認證方式由Servlet規范定制,服務端要存儲session信息需要占用內存資源,客戶端需要支持 cookie;

基于token的方式則一般不需要服務端存儲token,并且不限制客戶端的存儲方式。如今移動互聯網時代 更多類型的客戶端需要接入系統,系統多是采用前后端分離的架構進行實現,所以基于token的方式更適合。

1.3什么是授權

微信來舉例,微信登錄成功后用戶即可使用微信的功能,比如,發紅包、發朋友圈、添加好友等,沒有綁定 銀行卡的用戶是無法發送紅包的,綁定銀行卡的用戶才可以發紅包,發紅包功能、發朋友圈功能都是微信的資源即 功能資源,用戶擁有發紅包功能的權限才可以正常使用發送紅包功能,擁有發朋友圈功能的權限才可以使用發朋友 圈功能,這個根據用戶的權限來控制用戶使用資源的過程就是授權。

為什么要授權?

認證是為了保證用戶身份的合法性,授權則是為了更細粒度的對隱私數據進行劃分,授權是在認證通過后發生的, 控制不同的用戶能夠訪問不同的資源。

授權

授權是用戶認證通過根據用戶的權限來控制用戶訪問資源的過程,擁有資源的訪問權限則正常訪問,沒有權限則拒絕訪問。

1.4授權的數據模型

如何進行授權即如何對用戶訪問資源進行控制,首先需要學習授權相關的數據模型。

授權可簡單理解為Who對What(which)進行How操作,包括如下:

Who,即主體(Subject),主體一般是指用戶,也可以是程序,需要訪問系統中的資源。

What,即資源 (Resource),如系統菜單、頁面、按鈕、代碼方法、系統商品信息、系統訂單信息等。系統菜單、頁面、按鈕、代碼方法都屬于系統功能資源,對于web系統每個功能資源通常對應一個URL;系統商品信息、系統訂單信息都屬于實體資源(數據資源),實體資源由資源類型和資源實例組成,比如商品信息為資源類型,商品編號 為001的商品為資源實例。

How,權限/許可(Permission),規定了用戶對資源的操作許可,權限離開資源沒有意義, 如用戶查詢權限、用戶添加權限、某個代碼方法的調用權限、編號為001的用戶的修改權限等,通過權限可知用戶 對哪些資源都有哪些操作許可。

主體、資源、權限相關的數據模型如下:

主體(用戶id、賬號、密碼、…)

資源(資源id、資源名稱、訪問地址、…)

權限(權限id、權限標識、權限名稱、資源id、…)

角色(角色id、角色名稱、…)

角色和權限關系(角色id、權限id、…)

主體(用戶)和角色關系(用戶id、角色id、…)

主體(用戶)、資源、權限關系如下圖:

通常企業開發中將資源和權限表合并為一張權限表,如下:

資源(資源id、資源名稱、訪問地址、…)

權限(權限id、權限標識、權限名稱、資源id、…)

合并為:

權限(權限id、權限標識、權限名稱、資源名稱、資源訪問地址、…)

修改后數據模型之間的關系如下圖:

1.4 RBAC

如何實現授權?業界通常基于RBAC實現授權。

1.4.1 基于角色的訪問控制

RBAC基于角色的訪問控制(Role-Based Access Control)是按角色進行授權,比如:主體的角色為總經理可以查 詢企業運營報表,查詢員工工資信息等,訪問控制流程如下:

根據上邊的例子發現,當需要修改角色的權限時就需要修改授權的相關代碼,系統可擴展性差。

1.4.2 基于資源的訪問控制

RBAC基于資源的訪問控制(Resource-Based Access Control)是按資源(或權限)進行授權,比如:用戶必須 具有查詢工資權限才可以查詢員工工資信息等,訪問控制流程如下:

優點:系統設計時定義好查詢工資的權限標識,即使查詢工資所需要的角色變化為總經理和部門經理也不需要修改

授權代碼,系統可擴展性強。

2、基于Session的認證方式

認證流程

基于Session認證方式的流程是,用戶認證成功后,在服務端生成用戶相關的數據保存在session(當前會話),而發 給客戶端的 sesssion_id 存放到 cookie 中,這樣用客戶端請求時帶上 session_id 就可以驗證服務器端是否存在 session 數據,以此完成用戶的合法校驗。當用戶退出系統或session過期銷毀時,客戶端的session_id也就無效了。

基于Session的認證機制由Servlet規范定制,Servlet容器已實現,用戶通過HttpSession的操作方法即可實現,如 下是HttpSession相關的操作API。

3、整合案例

3.1 SpringMVC+Servlet3.0模擬認證、會話、授權

3.1.1引入依賴

1、由于是web工程,packaging設置為war

2、使用tomcat7-maven-plugin插件來運行工程

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.0.1</version><scope>provided</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency></dependencies>

3.1.2 Spring 容器配置ApplicationConfig

在confifig包下定義ApplicationConfig.java,它對應web.xml中ContextLoaderListener的配置

@Configuration //相當于applicationContext.xml @ComponentScan(basePackages = "com.cfl",excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)}) public class ApplicationConfig {//在此配置 除了Controller的其它bean,比如:數據庫鏈接池、事務管理器、業務bean等。 }

3.1.3 servletContext配置 WebConfig

本案例采用Servlet3.0無web.xml方式,的confifig包下定義WebConfifig.java,它對應s對應于DispatcherServlet配置。

@Configuration //就相當于springmvc.xml文件 @EnableWebMvc @ComponentScan(basePackages = "com.cfl",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)}) public class WebConfig implements WebMvcConfigurer {//視頻解析器@Beanpublic InternalResourceViewResolver viewResolver(){InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/view/");viewResolver.setSuffix(".jsp");return viewResolver;}}

3.1.4 加載 Spring容器SpringApplicationInitializer

在init包下定義Spring容器初始化類SpringApplicationInitializer,此類實現WebApplicationInitializer接口, Spring容器啟動時加載WebApplicationInitializer接口的所有實現類。

public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {//spring容器,相當于加載 applicationContext.xml@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{ApplicationConfig.class, WebSecurityConfig.class};}//servletContext,相當于加載springmvc.xml@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{WebConfig.class};}//url-mapping@Overrideprotected String[] getServletMappings() {return new String[]{"/"};} }

SpringApplicationInitializer相當于web.xml,使用了servlet3.0開發則不需要再定義web.xml, ApplicationConfifig.class對應以下配置的application-context.xml,WebConfifig.class對應以下配置的spring- mvc.xml,web.xml的內容參考:

<web‐app><listener><listener‐class>org.springframework.web.context.ContextLoaderListener</listener‐class></listener><context‐param> <param‐name>contextConfigLocation</param‐name> <param‐value>/WEB‐INF/application‐context.xml</param‐value> </context‐param> <servlet><servlet‐name>springmvc</servlet‐name> <servlet‐class>org.springframework.web.servlet.DispatcherServlet</servlet‐class> <init‐param> <param‐name>contextConfigLocation</param‐name><param‐value>/WEB‐INF/spring‐mvc.xml</param‐value></init‐param> <load‐on‐startup>1</load‐on‐startup> </servlet> <servlet‐mapping> <servlet‐name>springmvc</servlet‐name> <url‐pattern>/</url‐pattern> </servlet‐mapping> </web‐app>

3.1.5實現認證功能

在WebConfifig中新增如下配置,將/直接導向login.jsp頁面:

@Override // 視圖控制器public void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");}

認證接口

用戶進入認證頁面,輸入賬號和密碼,點擊登錄,請求/login進行身份認證。

(1)定義認證接口,此接口用于對傳來的用戶名、密碼校驗,若成功則返回該用戶的詳細信息,否則拋出錯誤異常:

認證服務: public interface AuthenticationService {/*** 用戶認證* @param authenticationRequest 用戶認證請求,賬號和密碼* @return 認證成功的用戶信息*/UserDto authentication(AuthenticationRequest authenticationRequest); }認證請求結構: @Data public class AuthenticationRequest {//認證請求參數,賬號、密碼。。/*** 用戶名*/private String username;/*** 密碼*/private String password;}認證成功后返回的用戶詳細信息,也就是當前登錄用戶的信息: @Data @AllArgsConstructor public class UserDto {public static final String SESSION_USER_KEY = "_user";//用戶身份信息private String id;private String username;private String password;private String fullname;private String mobile;/*** 用戶權限*/private Set<String> authorities;}

(2)認證實現類,根據用戶名查找用戶信息,并校驗密碼,這里模擬了兩個用戶:

package com.cfl.service;import com.cfl.model.AuthenticationRequest; import com.cfl.model.UserDto; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils;import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set;/*** @author Administrator* @version 1.0**/ @Service public class AuthenticationServiceImpl implements AuthenticationService{/*** 用戶認證,校驗用戶身份信息是否合法** @param authenticationRequest 用戶認證請求,賬號和密碼* @return 認證成功的用戶信息*/@Overridepublic UserDto authentication(AuthenticationRequest authenticationRequest) {//校驗參數是否為空if(authenticationRequest == null|| StringUtils.isEmpty(authenticationRequest.getUsername())|| StringUtils.isEmpty(authenticationRequest.getPassword())){throw new RuntimeException("賬號和密碼為空");}//根據賬號去查詢數據庫,這里測試程序采用模擬方法UserDto user = getUserDto(authenticationRequest.getUsername());//判斷用戶是否為空if(user == null){throw new RuntimeException("查詢不到該用戶");}//校驗密碼if(!authenticationRequest.getPassword().equals(user.getPassword())){throw new RuntimeException("賬號或密碼錯誤");}//認證通過,返回用戶身份信息return user;}//根據賬號查詢用戶信息private UserDto getUserDto(String userName){return userMap.get(userName);}//用戶信息private Map<String,UserDto> userMap = new HashMap<>();{Set<String> authorities1 = new HashSet<>();authorities1.add("p1");//這個p1我們人為讓它和/r/r1對應Set<String> authorities2 = new HashSet<>();authorities2.add("p2");//這個p2我們人為讓它和/r/r2對應userMap.put("zhangsan",new UserDto("1010","zhangsan","123","張三","133443",authorities1));userMap.put("lisi",new UserDto("1011","lisi","456","李四","144553",authorities2));} }

(3)登錄Controller,對/login請求處理,它調用AuthenticationService完成認證并返回登錄結果提示信息:

@RestController public class LoginController {@AutowiredAuthenticationService authenticationService;@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")public String login(AuthenticationRequest authenticationRequest, HttpSession session){UserDetails userDetails = authenticationService.authentication(authenticationRequest); return userDetails.getFullname() + " 登錄成功";} }

啟動項目,訪問路徑地址,進行測試

3.1.6實現會話功能

會話是指用戶登入系統后,系統會記住該用戶的登錄狀態,他可以在系統連續操作直到退出系統的過程。

認證的目的是對系統資源的保護,每次對資源的訪問,系統必須得知道是誰在訪問資源,才能對該請求進行合法性 攔截。因此,在認證成功后,一般會把認證成功的用戶信息放入Session中,在后續的請求中,系統能夠從Session 中獲取到當前用戶,用這樣的方式來實現會話機制。

增加會話控制 :首先在UserDto中定義一個SESSION_USER_KEY,作為Session中存放登錄用戶信息的key。

public static final String SESSION_USER_KEY = "_user";

然后修改LoginController,認證成功后,將用戶信息放入當前會話。并增加用戶登出方法,登出時將session置為失效。

@RequestMapping(value = "/login",produces = "text/plain;charset=utf-8")public String login(AuthenticationRequest authenticationRequest, HttpSession session){UserDto userDto = authenticationService.authentication(authenticationRequest);//存入sessionsession.setAttribute(UserDto.SESSION_USER_KEY,userDto);return userDto.getUsername() +"登錄成功";}@GetMapping(value = "/logout",produces = {"text/plain;charset=UTF-8"})public String logout(HttpSession session){session.invalidate();return "退出成功";}

啟動項目,訪問路徑地址,進行測試

3.1.7實現授權功能

現在我們已經完成了用戶身份憑證的校驗以及登錄的狀態保持,并且我們也知道了如何獲取當前登錄用戶(從 Session中獲取)的信息,接下來,用戶訪問系統需要經過授權,即需要完成如下功能:

匿名用戶(未登錄用戶)訪問攔截:禁止匿名用戶訪問某些資源。

登錄用戶訪問攔截:根據用戶的權限決定是否能訪問某些資源。

(1)增加權限數據

為了實現這樣的功能,我們需要在UserDto里增加權限屬性,用于表示該登錄用戶所擁有的權限,同時修改UserDto的構造方法。

并在AuthenticationServiceImpl中為模擬用戶初始化權限,其中張三給了p1權限,李四給了p2權限。

(2)增加測試資源

我們想實現針對不同的用戶能訪問不同的資源,前提是得有多個資源,因此在LoginController中增加測試資源2。

如上代碼所示

(3)實現授權攔截器

在interceptor包下定義SimpleAuthenticationInterceptor攔截器,實現授權攔截:

1、校驗用戶是否登錄

2、校驗用戶是否擁有操作權限

@Component public class SimpleAuthenticationInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {Object o = request.getSession().getAttribute(UserDto.SESSION_USER_KEY);if (Objects.isNull(o)) {writeContent(response,"請登錄");}UserDto userDto = (UserDto) o;String requestURI = request.getRequestURI();if( userDto.getAuthorities().contains("p1") && requestURI.contains("/r/r1")){return true;}if( userDto.getAuthorities().contains("p2") && requestURI.contains("/r/r2")){return true;}writeContent(response,"沒有權限,拒絕訪問");return false;}private void writeContent(HttpServletResponse response, String msg) {response.setContentType("text/html;charset=utf-8");try {PrintWriter writer = response.getWriter();writer.print(msg);writer.close();} catch (IOException e) {System.out.println("e = " + e);}} }

**在WebConfifig中配置攔截器,匹配/r/的資源為受保護的系統資源,訪問該資源的請求進入 SimpleAuthenticationInterceptor攔器。

@Configuration//就相當于springmvc.xml文件 @EnableWebMvc @ComponentScan(basePackages = "com.cfl",includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)}) public class WebConfig implements WebMvcConfigurer {@AutowiredSimpleAuthenticationInterceptor simpleAuthenticationInterceptor; // 自定義攔截器@Bean // 視圖解析器public InternalResourceViewResolver viewResolver(){InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/view/");viewResolver.setSuffix(".jsp");return viewResolver;}@Override // 視圖控制器public void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/").setViewName("login");}@Override // 攔截器public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(simpleAuthenticationInterceptor).addPathPatterns("/r/**");} }

測試

未登錄情況下,/r/r1與/r/r2均提示 “請先登錄”。

張三登錄情況下,由于張三有p1權限,因此可以訪問/r/r1,張三沒有p2權限,訪問/r/r2時提示 “權限不足 “。

李四登錄情況下,由于李四有p2權限,因此可以訪問/r/r2,李四沒有p1權限,訪問/r/r1時提示 “權限不足 “。

小結

基于Session的認證方式是一種常見的認證方式,至今還有非常多的系統在使用。我們在此小節使用Spring mvc技 術對它進行簡單實現,旨在讓大家更清晰實在的了解用戶認證、授權以及會話的功能意義及實現套路,也就是它們 分別干了哪些事兒?大概需要怎么做?

而在正式生產項目中,我們往往會考慮使用第三方安全框架(如 spring security,shiro等安全框架)來實現認證 授權功能,因為這樣做能一定程度提高生產力,提高軟件標準化程度,另外往往這些框架的可擴展性考慮的非常全 面。但是缺點也非常明顯,這些通用化組件為了提高支持范圍會增加很多可能我們不需要的功能,結構上也會比較 抽象,如果我們不夠了解它,一旦出現問題,將會很難定位。

3.2 Spring集成Security

Spring Security是一個能夠為基于Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。由于它 是Spring生態系統中的一員,因此它伴隨著整個Spring生態系統不斷修正、升級,在spring boot項目中加入spring security更是十分簡單,使用Spring Security 減少了為企業系統安全控制編寫大量重復代碼的工作。

代碼步驟略

<dependencies><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-web</artifactId><version>5.1.4.RELEASE</version></dependency><dependency><groupId>org.springframework.security</groupId><artifactId>spring-security-config</artifactId><version>5.1.4.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.5.RELEASE</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.0.1</version><scope>provided</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.8</version></dependency></dependencies>

小結

通過快速上手,使用Spring Security實現了認證和授權,Spring Security提供了基于賬號和密碼的認證方式, 通過安全配置即可實現請求攔截,授權功能,Spring Security能完成的不僅僅是這些。

3.3 SpringBoot集成Security

Spring Boot是一套Spring的快速開發框架,基于Spring 4.0設計,使用Spring Boot開發可以避免一些繁瑣的工程 搭建和配置,同時它集成了大量的常用框架,快速導入依賴包,避免依賴包的沖突。基本上常用的開發框架都支持 Spring Boot開發,例如:MyBatis、Dubbo等,Spring 家族更是如此,例如:Spring cloud、Spring mvc、 Spring security等,使用Spring Boot開發可以大大得高生產率,所以Spring Boot的使用率非常高。

學習如何通過Spring Boot開發Spring Security應用,Spring Boot提供spring-boot-starter-security用于開發Spring Security應用。

引入依賴

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.3.RELEASE</version></parent><dependencies><!-- 以下是>spring boot依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 以下是>spring security依賴--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency><!-- 以下是jsp依賴--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><scope>provided</scope></dependency><!--jsp頁面使用jstl標簽 --><dependency><groupId>javax.servlet</groupId><artifactId>jstl</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency><!--用于編譯jsp --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-jasper</artifactId><scope>provided</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency></dependencies>

SpringBoot工程啟動會自動掃描啟動類所在包下的所有Bean,加載到spring容器。

Spring Boot配置文件 :在resources下添加application.properties

server.port=8080 server.servlet.context-path=/security-springboot spring.application.name = security-springboot# 視頻解析器配置在application.properties中 spring.mvc.view.prefix=/WEB-INF/view/ spring.mvc.view.suffix=.jspspring.datasource.url=jdbc:mysql://localhost:3306/test spring.datasource.username= spring.datasource.password= spring.datasource.driver-class-name=com.mysql.jdbc.Driver

Servlet Context配置:對于WebConfig, 由于Spring boot starter自動裝配機制,無需使用@EnableWebMvc @ComponentScan

安全配置 :對于WebSecurityConfig,由于Spring boot starter自動裝配機制,無需使用@EnableWebSecurity

詳細代碼略

4、工作原理

4.1結構總覽

Spring Security所解決的問題就是安全訪問控制,而安全訪問控制功能其實就是對所有進入系統的請求進行攔截, 校驗每個請求是否能夠訪問它所期望的資源。

根據前邊知識的學習,可以通過Filter或AOP等技術來實現,Spring Security對Web資源的保護是靠Filter實現的,所以從這個Filter來入手,逐步深入Spring Security原理。

當初始化Spring Security時,會創建一個名為 SpringSecurityFilterChain 的Servlet過濾器,

類型為org.springframework.security.web.FilterChainProxy,它實現了javax.servlet.Filter,因此外部的請求會經過此 類,

下圖是Spring Security過慮器鏈結構圖:

FilterChainProxy是一個代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各個Filter,同時 這些Filter作為Bean被Spring管理,它們是Spring Security核心,各有各的職責,但他們并不直接處理用戶的認證,也不直接處理用戶的授權,而是把它們交給了認證管理器(AuthenticationManager)和決策管理器 (AccessDecisionManager)進行處理,

下圖是FilterChainProxy相關類的UML圖示:

spring Security功能的實現主要是由一系列過濾器鏈相互配合完成。

下面介紹過濾器鏈中主要的幾個過濾器及其作用:

SecurityContextPersistenceFilter 這個Filter是整個攔截過程的入口和出口(也就是第一個和最后一個攔截 器),會在請求開始時從配置好的 SecurityContextRepository 中獲取 SecurityContext,然后把它設置給 SecurityContextHolder。在請求完成后將 SecurityContextHolder 持有的 SecurityContext 再保存到配置好 的 SecurityContextRepository,同時清除 securityContextHolder 所持有的 SecurityContext;

UsernamePasswordAuthenticationFilter 用于處理來自表單提交的認證。該表單必須提供對應的用戶名和密 碼,其內部還有登錄成功或失敗后進行處理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,這些都可以根據需求做相關改變;

FilterSecurityInterceptor 是用于保護web資源的,使用AccessDecisionManager對當前用戶進行授權訪問,前面已經詳細介紹過了;

ExceptionTranslationFilter 能夠捕獲來自 FilterChain 所有的異常,并進行處理。

但是它只會處理兩類異常: AuthenticationException 和 AccessDeniedException,其它的異常它會繼續拋出。

4.2認證流程

  • 用戶提交用戶名、密碼被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 過濾器獲取到, 封裝為請求Authentication,通常情況下是UsernamePasswordAuthenticationToken這個實現類。

  • 然后過濾器將Authentication提交至認證管理器(AuthenticationManager)進行認證

  • 認證成功后, AuthenticationManager 身份管理器返回一個被填充滿了信息的(包括上面提到的權限信息, 身份信息,細節信息,但密碼通常會被移除) Authentication 實例。

  • SecurityContextHolder 安全上下文容器將第3步填充了信息的 Authentication ,通過SecurityContextHolder.getContext().setAuthentication(…)方法,設置到其中。 可以看出AuthenticationManager接口(認證管理器)是認證相關的核心接口,也是發起認證的出發點,它 的實現類為ProviderManager。而Spring Security支持多種認證方式,因此ProviderManager維護著一個 List 列表,存放多種認證方式,最終實際的認證工作是由 AuthenticationProvider完成的。咱們知道web表單的對應的AuthenticationProvider實現類為 DaoAuthenticationProvider,它的內部又維護著一個UserDetailsService負責UserDetails的獲取。最終 AuthenticationProvider將UserDetails填充至Authentication。

  • 認證核心組件的大體關系如下:

    AuthenticationProvider接口

    通過前面的Spring Security認證流程我們得知,認證管理器(AuthenticationManager)委托 AuthenticationProvider完成認證工作。

    AuthenticationProvider是一個接口,定義如下:

    public interface AuthenticationProvider { Authentication authenticate(Authentication authentication) throws AuthenticationException;boolean supports(Class<?> var1); }

    authenticate()方法定義了認證的實現過程,它的參數是一個Authentication,里面包含了登錄用戶所提交的用 戶、密碼等。而返回值也是一個Authentication,這個Authentication則是在認證成功后,將用戶的權限及其他信 息重新組裝后生成。 Spring Security中維護著一個 List 列表,存放多種認證方式,不同的認證方式使用不 同的AuthenticationProvider。如使用用戶名密碼登錄時,使用AuthenticationProvider1,短信登錄時使用 AuthenticationProvider2等等這樣的例子很多。

    每個AuthenticationProvider需要實現**supports()**方法來表明自己支持的認證方式,如我們使用表單方式認證,

    在提交請求時Spring Security會生成UsernamePasswordAuthenticationToken,它是一個Authentication,里面 封裝著用戶提交的用戶名、密碼信息。而對應的,哪個AuthenticationProvider來處理它?

    我們在DaoAuthenticationProvider的基類AbstractUserDetailsAuthenticationProvider發現以下代碼:

    也就是說當web表單提交用戶名密碼時,Spring Security由DaoAuthenticationProvider處理。

    最后,我們來看一下Authentication(認證信息)的結構,它是一個接口,我們之前提到的UsernamePasswordAuthenticationToken就是它的實現之一:

    public interface Authentication extends Principal, Serializable {1Collection<? extends GrantedAuthority> getAuthorities();2Object getCredentials();3Object getDetails();4Object getPrincipal();5boolean isAuthenticated(); void setAuthenticated(boolean var1) throws IllegalArgumentException; }

    (1)Authentication是spring security包中的接口,直接繼承自Principal類,而Principal是位于 java.security 包中的。它是表示著一個抽象主體身份,任何主體都有一個名稱,因此包含一個getName()方法。

    (2)getAuthorities(),權限信息列表,默認是GrantedAuthority接口的一些實現類,通常是代表權限信息的一系 列字符串。

    (3)getCredentials(),憑證信息,用戶輸入的密碼字符串,在認證過后通常會被移除,用于保障安全。

    (4)getDetails(),細節信息,web應用中的實現接口通常為 WebAuthenticationDetails,它記錄了訪問者的ip地 址和sessionId的值。

    (5)getPrincipal(),身份信息,大部分情況下返回的是UserDetails接口的實現類,UserDetails代表用戶的詳細 信息,那從Authentication中取出來的UserDetails就是當前登錄用戶信息,它也是框架中的常用接口之一。

    UserDetailsService接口

    現在咱們現在知道DaoAuthenticationProvider處理了web表單的認證邏輯,認證成功后既得到一個 Authentication(UsernamePasswordAuthenticationToken實現),里面包含了身份信息(Principal)。這個身份 信息就是一個 Object,大多數情況下它可以被強轉為UserDetails對象。 DaoAuthenticationProvider中包含了一個UserDetailsService實例,它負責根據用戶名提取用戶信息 UserDetails(包含密碼),而后DaoAuthenticationProvider會去對比UserDetailsService提取的用戶密碼與用戶提交的密碼是否匹配作為認證成功的關鍵依據,因此可以通過將自定義的 UserDetailsService 公開為spring bean來定 義自定義身份驗證。

    public interface UserDetailsService {UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }

    DaoAuthenticationProvider和UserDetailsService的職責搞混淆,其實UserDetailsService只負責從特定 的地方(通常是數據庫)加載用戶信息,僅此而已。而DaoAuthenticationProvider的職責更大,它完成完整的認 證流程,同時會把UserDetails填充Authentication。

    上面一直提到UserDetails是用戶信息,咱們看一下它:

    public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities();String getPassword(); String getUsername();boolean isAccountNonExpired();boolean isAccountNonLocked(); boolean isCredentialsNonExpired();boolean isEnabled(); }

    它和Authentication接口很類似,比如它們都擁有username,authorities。

    Authentication的getCredentials()與 UserDetails中的getPassword()需要被區分對待,前者是用戶提交的密碼憑證,后者是用戶實際存儲的密碼,認證其實就是對這兩者的比對。Authentication中的getAuthorities()實際是由UserDetails的getAuthorities()傳遞而形成的。

    還記得Authentication接口中的getDetails()方法嗎?其中的UserDetails用戶詳細信息便是經過了 AuthenticationProvider認證之后被填充的。

    通過實現UserDetailsService和UserDetails,我們可以完成對用戶信息獲取方式以及用戶信息字段的擴展。

    Spring Security提供的InMemoryUserDetailsManager(內存認證),JdbcUserDetailsManager(jdbc認證)就是UserDetailsService的實現類,主要區別無非就是從內存還是從數據庫加載用戶

    自定義UserDetailsService

    @Service public class SpringDataUserDetailsService implements UserDetailsService {@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//登錄賬號System.out.println("username="+username); //根據賬號去數據庫查詢...//這里暫時使用靜態數據 UserDetails userDetails = User.withUsername(username).password("123").authorities("p1").build(); return userDetails; } }

    屏蔽安全配置類中UserDetailsService的定義

    /* @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build()); manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());return manager; }*/

    重啟工程,請求認證,SpringDataUserDetailsService的loadUserByUsername方法被調用 ,查詢用戶信息。

    PasswordEncoder接口

    DaoAuthenticationProvider認證處理器通過UserDetailsService獲取到UserDetails后,它是如何與請求 Authentication中的密碼做對比呢? 在這里Spring Security為了適應多種多樣的加密類型,又做了抽象,DaoAuthenticationProvider通過 PasswordEncoder接口的matches方法進行密碼的對比,而具體的密碼對比細節取決于實現:

    public interface PasswordEncoder { String encode(CharSequence var1); boolean matches(CharSequence var1, String var2);default boolean upgradeEncoding(String encodedPassword) {return false;} }

    而Spring Security提供很多內置的PasswordEncoder,能夠開箱即用,使用某種PasswordEncoder只需要進行如 下聲明即可,如下:

    @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); }

    **NoOpPasswordEncoder采用字符串匹配方法,不對密碼進行加密比較處理,**密碼比較流程如下:

    1、用戶輸入密碼(明文 )

    2、DaoAuthenticationProvider獲取UserDetails(其中存儲了用戶的正確密碼)

    3、DaoAuthenticationProvider使用PasswordEncoder對輸入的密碼和正確的密碼進行校驗,密碼一致則校驗通過,否則校驗失敗。

    NoOpPasswordEncoder的校驗規則拿 輸入的密碼和UserDetails中的正確密碼進行字符串比較,字符串內容一致 則校驗通過,否則 校驗失敗。 實際項目中推薦使用BCryptPasswordEncoder, Pbkdf2PasswordEncoder, SCryptPasswordEncoder等。

    例如使用BCryptPasswordEncoder

    1、配置BCryptPasswordEncoder 在安全配置類中定義

    @Bean public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(); }

    測試認證失敗,提示:Encoded password does not look like BCrypt。

    原因: 由于UserDetails中存儲的是原始密碼(比如:123),它不是BCrypt格式。

    跟蹤 DaoAuthenticationProvider第33行代碼查看 userDetails中的內容 ,跟蹤第38行代碼查看 PasswordEncoder的類型。

    @RunWith(SpringRunner.class) public class TestBCrypt { @Test public void test1(){//對原始密碼加密 String hashpw = BCrypt.hashpw("123",BCrypt.gensalt());System.out.println(hashpw); //校驗原始密碼和BCrypt密碼是否一致 boolean checkpw = BCrypt.checkpw("123", "$2a$10$NlBC84MVb7F95EXYTXwLneXgCca6/GipyWR5NHm8K0203bSQMLpvm");System.out.println(checkpw); } }

    實際項目中存儲在數據庫中的密碼并不是原始密碼,都是經過加密處理的密碼。

    4.3授權流程

    通過快速上手我們知道,Spring Security可以通過 http.authorizeRequests() 對web請求進行授權保護。Spring Security使用標準Filter建立了對web請求的攔截,最終實現對資源的授權訪問。

    Spring Security的授權流程如下:

  • 攔截請求,已認證用戶訪問受保護的web資源將被SecurityFilterChain中的 FilterSecurityInterceptor 的子 類攔截。

  • 獲取資源訪問策略,FilterSecurityInterceptor會從 SecurityMetadataSource 的子類 DefaultFilterInvocationSecurityMetadataSource 獲取要訪問當前資源所需要的權限 Collection 。 SecurityMetadataSource其實就是讀取訪問策略的抽象,而讀取的內容,其實就是我們配置的訪問規則,讀取訪問策略如:

  • http.authorizeRequests() .antMatchers("/r/r1").hasAuthority("p1") .antMatchers("/r/r2").hasAuthority("p2") ...
  • 最后,FilterSecurityInterceptor會調用 AccessDecisionManager 進行授權決策,若決策通過,則允許訪問資 源,否則將禁止訪問。
  • AccessDecisionManager(訪問決策管理器)

    public interface AccessDecisionManager { /** 通過傳遞的參數來決定用戶是否有訪問對應受保護資源的權限 */void decide(Authentication authentication , Object object,Collection<ConfigAttribute> configAttributes ) throws AccessDeniedException, InsufficientAuthenticationException;//略.. }

    decide的參數:

    authentication:要訪問資源的訪問者的身份

    object:要訪問的受保護資源,web請求對應FilterInvocation

    configAttributes:是受保護資源的訪問策略,通過SecurityMetadataSource獲取。

    decide接口就是用來鑒定當前用戶是否有訪問對應受保護資源的權限。

    授權決策

    AccessDecisionManager采用投票的方式來確定是否能夠訪問受保護資源。

    通過上圖可以看出,AccessDecisionManager中包含的一系列AccessDecisionVoter將會被用來對Authentication

    是否有權訪問受保護對象進行投票,AccessDecisionManager根據投票結果,做出最終決策。

    AccessDecisionVoter是一個接口,其中定義有三個方法,具體結構如下所示。

    public interface AccessDecisionVoter<S> { int ACCESS_GRANTED = 1;int ACCESS_ABSTAIN = 0; int ACCESS_DENIED =1; boolean supports(ConfigAttribute var1);boolean supports(Class<?> var1); int vote(Authentication var1, S var2, Collection<ConfigAttribute> var3); }

    vote()方法的返回結果會是AccessDecisionVoter中定義的三個常量之一。

    ACCESS_GRANTED表示同意,

    ACCESS_DENIED表示拒絕,

    ACCESS_ABSTAIN表示棄權。

    如果一個AccessDecisionVoter不能判定當前 Authentication是否擁有訪問對應受保護對象的權限,則其vote()方法的返回值應當為棄權ACCESS_ABSTAIN。 Spring Security內置了三個基于投票的AccessDecisionManager實現類如下,它們分別是 :

    AffirmativeBasedConsensusBasedUnanimousBased

    AffirmativeBased的邏輯:

    (1)只要有AccessDecisionVoter的投票為ACCESS_GRANTED則同意用戶進行訪問;

    (2)如果全部棄權也表示通過;

    (3)如果沒有一個人投贊成票,但是有人投反對票,則將拋出AccessDeniedException。

    Spring security默認使用的是AffirmativeBased。

    ConsensusBased的邏輯:

    (1)如果贊成票多于反對票則表示通過。

    (2)反過來,如果反對票多于贊成票則將拋出AccessDeniedException。

    (3)如果贊成票與反對票相同且不等于0,并且屬性allowIfEqualGrantedDeniedDecisions的值為true,則表 示通過,否則將拋出異常AccessDeniedException。參數allowIfEqualGrantedDeniedDecisions的值默認為true。

    (4)如果所有的AccessDecisionVoter都棄權了,則將視參數allowIfAllAbstainDecisions的值而定,如果該值 為true則表示通過,否則將拋出異常AccessDeniedException。參數allowIfAllAbstainDecisions的值默認為false。

    UnanimousBased的邏輯:

    與另外兩種實現有點不一樣,另外兩種會一次性把受保護對象的配置屬性全部傳遞 給AccessDecisionVoter進行投票,而UnanimousBased會一次只傳遞一個ConfigAttribute給 AccessDecisionVoter進行投票。

    這也就意味著如果我們的AccessDecisionVoter的邏輯是只要傳遞進來的 ConfigAttribute中有一個能夠匹配則投贊成票,但是放到UnanimousBased中其投票結果就不一定是贊成了。

    具體來說是這樣的:

    (1)如果受保護對象配置的某一個ConfigAttribute被任意的AccessDecisionVoter反對了,則將拋出 AccessDeniedException。

    (2)如果沒有反對票,但是有贊成票,則表示通過。

    (3)如果全部棄權了,則將視參數allowIfAllAbstainDecisions的值而定,true則通過,false則拋出AccessDeniedException。

    Spring Security也內置一些投票者實現類如RoleVoterAuthenticatedVoterWebExpressionVoter等可以查閱資料進行學習。

    5、自定認證

    Spring Security提供了非常好的認證擴展方法,比如:快速上手中將用戶信息存儲到內存中,實際開發中用戶信息 通常在數據庫,Spring security可以實現從數據庫讀取用戶信息,Spring security還支持多種授權方法。

    5.1登錄

    快速上手中,你可能會想知道登錄頁面從哪里來的?因為我們并沒有提供任何的HTML或JSP文件。Spring Security的默認配置沒有明確設定一個登錄頁面的URL,因此Spring Security會根據啟用的功能自動生成一個登錄 頁面URL,并使用默認URL處理登錄的提交內容,登錄后跳轉的到默認URL等等。盡管自動生成的登錄頁面很方便快速啟動和運行,但大多數應用程序都希望定義自己的登錄頁面。

    配置認證頁面 :在WebConfifig.java中配置認證頁面地址:

    //默認Url根路徑跳轉到/login,此url為spring security提供 @Overridepublic void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("redirect:/login‐view");registry.addViewController("/login‐view").setViewName("login"); }

    安全配置 :在WebSecurityConfig中配置表章登錄信息

    //配置安全攔截機制 @Override protected void configure(HttpSecurity http) throws Exception {http .authorizeRequests() .antMatchers("/r/**").authenticated() .anyRequest().permitAll() .and() .formLogin() // 允許表單登錄.loginPage("/login‐view") //指定我們自己的登錄頁,spring security以重定向方式跳轉到/login-view.loginProcessingUrl("/login") //指定登錄處理的URL,也就是用戶名、密碼表單提交的目的路徑.successForwardUrl("/login‐success") //指定登錄成功后的跳轉URL.permitAll(); //允許 任意用戶訪問基于表單登錄的所有的URL }

    測試 :當用戶沒有認證時訪問系統的資源會重定向到login-view頁面

    問題解決:

    spring security為防止CSRF(Cross-site request forgery跨站請求偽造)的發生,限制了除了get以外的大多數方 法。

    解決方法1: 屏蔽CSRF控制,即spring security不再限制CSRF。 配置WebSecurityConfig

    @Override protected void configure(HttpSecurity http) throws Exception {http.csrf().disable() //屏蔽CSRF控制,即spring security不再限制CSRF...... }

    解決方法2:

    在login.jsp頁面添加一個token,spring security會驗證token,如果token合法則可以繼續請求。 修改login.jsp

    <form action="login" method="post"> <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>...... </form>

    連接數據庫認證

    定義dataSource 在application.properties配置

    spring.datasource.url=jdbc:mysql://localhost:3306/user_db spring.datasource.username=root spring.datasource.password= spring.datasource.driver‐class‐name=com.mysql.jdbc.Driver

    UserDao

    @Repository public class UserDao {@AutowiredJdbcTemplate jdbcTemplate;//根據賬號查詢用戶信息public UserDto getUserByUsername(String username){String sql = "select id,username,password,fullname,mobile from t_user where username = ?";//連接數據庫查詢用戶List<UserDto> list = jdbcTemplate.query(sql, new Object[]{username}, new BeanPropertyRowMapper<>(UserDto.class));if(list !=null && list.size()==1){return list.get(0);}return null;}}

    定義UserDetailService

    @Service public class SpringDataUserDetailsService implements UserDetailsService { @Autowired UserDao userDao; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //登錄賬號 System.out.println("username="+username);//根據賬號去數據庫查詢... UserDto user = userDao.getUserByUsername(username); if(user == null){return null;}//這里暫時使用靜態數據UserDetails userDetails = User.withUsername(user.getFullname()).password(user.getPassword()).authorities("p1").build(); return userDetails; } }

    測試 :輸入賬號和密碼請求認證,跟蹤代碼。

    使用BCryptPasswordEncoder

    使用BCryptPasswordEncoder需要完成如下工作:

    在安全配置類中定義BCryptPasswordEncoder

    @Bean public PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder(); }

    UserDetails中的密碼存儲BCrypt格式 ,前邊實現了從數據庫查詢用戶信息,所以數據庫中的密碼應該存儲BCrypt格式

    5.2會話

    用戶認證通過后,為了避免用戶的每次操作都進行認證可將用戶的信息保存在會話中。spring security提供會話管理,認證通過后將身份信息放入SecurityContextHolder上下文,SecurityContext與當前線程進行綁定,方便獲取用戶身份。

    獲取用戶身份

    @RestController public class LoginController {}

    會話控制

    我們可以通過以下選項準確控制會話何時創建以及Spring Security如何與之交互:

    機制描述
    always如果沒有session存在就創建一個
    ifRequired如果需要就創建一個Session(默認)登錄時
    neverSpringSecurity 將不會創建Session,但是如果應用中其他地方創建了Session,那么Spring
    statelessSpringSecurity將絕對不會創建Session,也不使用Session

    通過以下配置方式對該選項進行配置:

    @Override protected void configure(HttpSecurity http) throws Exception {http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) }

    默認情況下,Spring Security會為每個登錄成功的用戶會新建一個Session,就是ifRequired

    若選用never,則指示Spring Security對登錄成功的用戶不創建Session了,但若你的應用程序在某地方新建了 session,那么Spring Security會用它的。

    若使用stateless,則說明Spring Security對登錄成功的用戶不會創建Session了,你的應用程序也不會允許新建 session。并且它會暗示不使用cookie,所以每個請求都需要重新進行身份驗證。這種無狀態架構適用于REST API 及其無狀態認證機制。

    會話超時

    可以再sevlet容器中設置Session的超時時間,如下設置Session有效期為3600s;

    spring boot 配置文件:

    server.servlet.session.timeout=3600s

    session超時之后,可以通過Spring Security 設置跳轉的路徑。

    http.sessionManagement() .expiredUrl("/login‐view?error=EXPIRED_SESSION").invalidSessionUrl("/login‐view?error=INVALID_SESSION");// expired指session過期,invalidSession指傳入的sessionid無效。

    安全會話cookie

    我們可以使用httpOnly和secure標簽來保護我們的會話cookie:

    httpOnly:如果為true,那么瀏覽器腳本將無法訪問cookie

    secure:如果為true,則cookie將僅通過HTTPS連接發送

    spring boot 配置文件:

    server.servlet.session.cookie.http‐only=true server.servlet.session.cookie.secure=true

    5.3退出

    Spring security默認實現了logout退出,訪問/logout

    點擊“Log Out”退出 成功。 退出后訪問其它url判斷是否成功退出。

    這里也可以自定義退出成功的頁面: 在WebSecurityConfifig的protected void confifigure(HttpSecurity http)中配置:

    .and() .logout() .logoutUrl("/logout") .logoutSuccessUrl("/login‐view?logout");

    當退出操作出發時,將發生:

    使HTTP Session 無效

    清除 SecurityContextHolder

    跳轉到 /login-view?logout

    但是,類似于配置登錄功能,可以進一步自定義退出功能

    @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests()//... .and() .logout() //提供系統退出支持,使用 WebSecurityConfigurerAdapter 會自動被應用.logoutUrl("/logout") //設置觸發退出操作的URL (默認是 /logout ).logoutSuccessUrl("/login‐view?logout") //退出之后跳轉的URL。默認是 /login?logout 。.logoutSuccessHandler(logoutSuccessHandler) //定制的 LogoutSuccessHandler ,用于實現用戶退出成功時的處理。如果指定了這個選項那么 logoutSuccessUrl() 的設置會被忽略。.addLogoutHandler(logoutHandler)//添加一個 LogoutHandler ,用于實現用戶退出時的清理工作.默認 SecurityContextLogoutHandler 會被添加 為最后一個 LogoutHandler 。.invalidateHttpSession(true); //指定是否在退出時讓 HttpSession 無效。 默認設置為 true。 }

    注意:

    如果讓logout在GET請求下生效,必須關閉防止CSRF攻擊csrf().disable()。如果開啟了CSRF,必須使用 post方式請求/logout

    logoutHandler

    一般來說, LogoutHandler 的實現類被用來執行必要的清理,因而他們不應該拋出異常。

    下面是Spring Security提供的一些實現:

    PersistentTokenBasedRememberMeServices 基于持久化token的RememberMe功能的相關清理

    TokenBasedRememberMeService 基于token的RememberMe功能的相關清理

    CookieClearingLogoutHandler 退出時Cookie的相關清理

    CsrfLogoutHandler 負責在退出時移除csrfToken

    SecurityContextLogoutHandler 退出時SecurityContext的相關清理

    鏈式API提供了調用相應的 LogoutHandler 實現的快捷方式,比如deleteCookies()

    5.4授權

    授權的方式包括 web授權和方法授權,web授權是通過 url攔截進行授權,方法授權是通過 方法攔截進行授權。

    他們都會調用accessDecisionManager進行授權決策,若為web授權則攔截器為FilterSecurityInterceptor;

    若為方 法授權則攔截器為MethodSecurityInterceptor。如果同時通過web授權和方法授權則先執行web授權,再執行方法授權,

    最后決策通過,則允許訪問資源,否則將禁止訪問。

    類關系如下:

    web授權

    在上面例子中我們完成了認證攔截,并對/r/**下的某些資源進行簡單的授權保護,但是我們想進行靈活的授權控 制該怎么做呢?

    通過給 http.authorizeRequests() 添加多個子節點來定制需求到我們的URL,如下代碼:

    @Override protected void configure(HttpSecurity http) throws Exception {http .authorizeRequests() (1) .antMatchers("/r/r1").hasAuthority("p1") (2) .antMatchers("/r/r2").hasAuthority("p2") (3).antMatchers("/r/r3").access("hasAuthority('p1') and hasAuthority('p2')") (4) .antMatchers("/r/**").authenticated() (5).anyRequest().permitAll() (6) .and() .formLogin() // ... }

    (1) http.authorizeRequests() 方法有多個子節點,每個macher按照他們的聲明順序執行。

    (2)指定"/r/r1"URL,擁有p1權限能夠訪問

    (3)指定"/r/r2"URL,擁有p2權限能夠訪問

    (4)指定了"/r/r3"URL,同時擁有p1和p2權限才能夠訪問

    (5)指定了除了r1、r2、r3之外"/r/**"資源,同時通過身份認證就能夠訪問,這里使用SpEL(Spring Expression Language)表式

    (6)剩余的尚未匹配的資源,不做保護

    注意

    規則的順序是重要的,更具體的規則應該先寫現在以/ admin開始的所有內容都需要具有ADMIN角色的身份驗證用 戶,

    即使是/ admin / login路徑(因為/ admin / login已經被/ admin / **規則匹配,因此第二個規則被忽略)

    .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/admin/login").permitAll()

    因此,登錄頁面的規則應該在/ admin / **規則之前.例如

    .antMatchers("/admin/login").permitAll() .antMatchers("/admin/**").hasRole("ADMIN")

    保護URL常用的方法有:

    authenticated() 保護URL,需要用戶登錄

    permitAll() 指定URL無需保護,一般應用與靜態資源文件

    hasRole(String role) 限制單個角色訪問,角色將被增加 “ROLE_” .所以”ADMIN” 將和 “ROLE_ADMIN”進行比較.

    hasAuthority(String authority) 限制單個權限訪問

    **hasAnyRole(String… roles)**允許多個角色訪問.

    hasAnyAuthority(String… authorities) 允許多個權限訪問.

    access(String attribute) 該方法使用 SpEL表達式, 所以可以創建復雜的限制.

    hasIpAddress(String ipaddressExpression) 限制IP地址或子網

    方法授權

    現在我們已經掌握了使用如何使用 http.authorizeRequests() 對web資源進行授權保護,從Spring Security2.0版 本開始,它支持服務層方法的安全性的支持。

    @PreAuthorize,@PostAuthorize, @Secured三類注解。 我們可以在任何 @Configuration 實例上使用 @EnableGlobalMethodSecurity 注釋來啟用基于注解的安全性。

    以下內容將啟用Spring Security的 @Secured 注釋。

    @EnableGlobalMethodSecurity(securedEnabled = true) public class MethodSecurityConfig {// ...}

    然后向方法(在類或接口上)添加注解就會限制對該方法的訪問。 Spring Security的原生注釋支持為該方法定義了 一組屬性。 這些將被傳遞給AccessDecisionManager以供它作出實際的決定:

    public interface BankService { @Secured("IS_AUTHENTICATED_ANONYMOUSLY")public Account readAccount(Long id);@Secured("IS_AUTHENTICATED_ANONYMOUSLY") public Account[] findAccounts(); @Secured("ROLE_TELLER") public Account post(Account account, double amount); }

    以上配置標明readAccount、fifindAccounts方法可匿名訪問,底層使用WebExpressionVoter投票器,可從AffirmativeBased第23行代碼跟蹤。post方法需要有TELLER角色才能訪問,底層使用RoleVoter投票器。

    使用如下代碼可啟用prePost注解的支持

    @EnableGlobalMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig { // ... }

    相應Java代碼如下:

    public interface BankService {@PreAuthorize("isAnonymous()") public Account readAccount(Long id); @PreAuthorize("isAnonymous()") public Account[] findAccounts(); @PreAuthorize("hasAuthority('p_transfer') and hasAuthority('p_read_account')")public Account post(Account account, double amount); }

    以上配置標明readAccount、fifindAccounts方法可匿名訪問,

    post方法需要同時擁有p_transfer和p_read_account 權限才能訪問,

    底層使用WebExpressionVoter投票器,可從AffirmativeBased第23行代碼跟蹤。

    6、分布式系統認證方案

    6.1什么是分布式系統

    分布式系統具體如下基本特點:

    1、分布性:每個部分都可以獨立部署,服務之間交互通過網絡進行通信,比如:訂單服務、商品服務。

    2、伸縮性:每個部分都可以集群方式部署,并可針對部分結點進行硬件及軟件擴容,具有一定的伸縮能力。

    3、共享性:每個部分都可以作為共享資源對外提供服務,多個部分可能有操作共享資源的情況。

    4、開放性:每個部分根據需求都可以對外發布共享資源的訪問接口,并可允許第三方系統訪問。

    6.2 分布式認證需求

    分布式系統的每個服務都會有認證、授權的需求,如果每個服務都實現一套認證授權邏輯會非常冗余,

    考慮分布式系統共享性的特點,需要由獨立的認證服務處理系統認證授權的請求;

    考慮分布式系統開放性的特點,不僅對系統內部服務提供認證,對第三方系統也要提供認證。

    分布式認證的需求總結如下:

    統一認證授權

    提供獨立的認證服務,統一處理認證授權。無論是不同類型的用戶,還是不同種類的客戶端(web端,H5、APP),均采用一致的認證、權限、會話機制,實現統一認證授權。

    要實現統一則認證方式必須可擴展,支持各種認證需求,比如:用戶名密碼認證、短信驗證碼、二維碼、人臉識別 等認證方式,并可以非常靈活的切換。

    應用接入認證

    應提供擴展和開放能力,提供安全的系統對接機制,并可開放部分API給接入第三方使用,一方應用(內部 系統服 務)和三方應用(第三方應用)均采用統一機制接入。

    6.3 分布式認證方案

    選型分析

    1、基于session的認證方式

    在分布式的環境下,基于session的認證會出現一個問題,每個應用服務都需要在session中存儲用戶身份信息,通

    過負載均衡將本地的請求分配到另一個應用服務需要將session信息帶過去,否則會重新認證。

    這個時候,通常的做法有下面幾種:

    Session****復制:多臺應用服務器之間同步session,使session保持一致,對外透明。

    Session****黏貼:當用戶訪問集群中某臺服務器后,強制指定后續所有請求均落到此機器上。

    Session****集中存儲:將Session存入分布式緩存中,所有服務器應用實例統一從分布式緩存中存取Session。

    總體來講,基于session認證的認證方式,可以更好的在服務端對會話進行控制,且安全性較高。但是,session機

    制方式基于cookie,在復雜多樣的移動客戶端上不能有效的使用,并且無法跨域,另外隨著系統的擴展需提高

    session的復制、黏貼及存儲的容錯性。

    2、基于token的認證方式

    基于token的認證方式,服務端不用存儲認證數據,易維護擴展性強, 客戶端可以把token 存在任意地方,并且可

    以實現web和app統一認證機制。

    其缺點也很明顯,token由于自包含信息,因此一般數據量較大,而且每次請求 都需要傳遞,因此比較占帶寬。另外,token的簽名驗簽操作也會給cpu帶來額外的處理負擔。

    技術方案

    根據 選型的分析,決定采用基于token的認證方式,它的優點是:

    1、適合統一認證的機制,客戶端、一方應用、三方應用都遵循一致的認證機制。

    2、token認證方式對第三方應用接入更適合,因為它更開放,可使用當前有流行的開放協議Oauth2.0、JWT等。

    3、一般情況服務端無需存儲會話信息,減輕了服務端的壓力。

    分布式系統認證技術方案見下圖:

    流程描述:

    (1)用戶通過接入方(應用)登錄,接入方采取OAuth2.0方式在統一認證服務(UAA)中認證。

    (2)認證服務(UAA)調用驗證該用戶的身份是否合法,并獲取用戶權限信息。

    (3)認證服務(UAA)獲取接入方權限信息,并驗證接入方是否合法。

    (4)若登錄用戶以及接入方都合法,認證服務生成jwt令牌返回給接入方,其中jwt中包含了用戶權限及接入方權限。

    (5)后續,接入方攜帶jwt令牌對API網關內的微服務資源進行訪問。

    (6)API網關對令牌解析、并驗證接入方的權限是否能夠訪問本次請求的微服務。

    (7)如果接入方的權限沒問題,API網關將原請求header中附加解析后的明文Token,并將請求轉發至微服務。

    (8)微服務收到請求,明文token中包含登錄用戶的身份和權限信息。因此后續微服務自己可以干兩件事:

    ? 1,用 戶授權攔截(看當前用戶是否有權訪問該資源)

    ? 2,將用戶信息存儲進當前線程上下文(有利于后續業務邏輯隨時 獲取當前用戶信息)

    流程所涉及到UAA服務、API網關這三個組件職責如下:

    1)統一認證服務(UAA

    它承載了OAuth2.0接入方認證、登入用戶的認證、授權以及生成令牌的職責,完成實際的用戶認證、授權功能。

    2)API網關

    作為系統的唯一入口,API網關為接入方提供定制的API集合,它可能還具有其它職責,如身份驗證、監控、負載均 衡、緩存等。

    API網關方式的核心要點是,所有的接入方和消費端都通過統一的網關接入微服務,在網關層處理所有的非業務功能。

    7、OAuth2.0 開放授權

    7.1 OAuth2.0介紹

    OAuth(開放授權)是一個開放標準,允許用戶授權第三方應用訪問他們存儲在另外的服務提供者上的信息,而不 需要將用戶名和密碼提供給第三方應用或分享他們數據的所有內容。OAuth2.0是OAuth協議的延續版本,但不向 后兼容OAuth 1.0即完全廢止了OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH認證服 務,這些都足以說明OAUTH標準逐漸成為開放資源授權的標準。 Oauth協議目前發展到2.0版本,1.0版本過于復雜,2.0版本已得到廣泛應用。

    參考:https://baike.baidu.com/item/oAuth/7153134?fr=aladdin

    Oauth協議:https://tools.ietf.org/html/rfc6749

    OAauth2.0包括以下角色:

    1、客戶端

    本身不存儲資源,需要通過資源擁有者的授權去請求資源服務器的資源,

    比如:Android客戶端、Web客戶端(瀏 覽器端)、微信客戶端等。

    2、資源擁有者

    通常為用戶,也可以是應用程序,即該資源的擁有者。

    3、授權服務器(也稱認證服務器)

    用于服務提供商對資源擁有的身份進行認證、對訪問資源進行授權,認證成功后會給客戶端發放令牌 (access_token),

    作為客戶端訪問資源服務器的憑據。本例為微信的認證服務器。

    4、資源服務器

    存儲資源的服務器,本例子為微信存儲的用戶信息。

    現在還有一個問題,服務提供商能允許隨便一個客戶端就接入到它的授權服務器嗎?

    答案是否定的,服務提供商會給準入的接入方一個身份,用于接入時的憑據:

    client_id:客戶端標識 client_secret:客戶端秘鑰

    因此,準確來說,授權服務器對兩種OAuth2.0中的兩個角色進行認證授權,分別是資源擁有者客戶端

    7.2 Spring Cloud Security OAuth2 的實現

    Spring-Security-OAuth2是對OAuth2的一種實現,并且跟我們之前學習的Spring Security相輔相成,與Spring Cloud體系的集成也非常便利,學習它,最終使用它來實現我們設計的分布式認證授權解決方案。

    OAuth2.0的服務提供方涵蓋兩個服務,即授權服務 (Authorization Server,也叫認證服務) 和資源服務 (Resource Server),使用 Spring Security OAuth2 的時候你可以選擇把它們在同一個應用程序中實現,也可以選擇建立使用 同一個授權服務的多個資源服務。

    授權服務 **(Authorization Server)**應包含對接入端以及登入用戶的合法性進行驗證并頒發token等功能,

    對令牌的請求端點由 Spring MVC 控制器進行實現,下面是配置一個認證服務必須要實現的endpoints:

    AuthorizationEndpoint 服務于認證請求。默認 URL: /oauth/authorize 。

    TokenEndpoint 服務于訪問令牌的請求。默認 URL: /oauth/token 。

    資源服務 (Resource Server),應包含對資源的保護功能,對非法請求進行攔截,對請求中token進行解析鑒 權等,

    下面的過濾器用于實現 OAuth 2.0 資源服務:

    OAuth2AuthenticationProcessingFilter用來對請求給出的身份令牌解析鑒權。

    7.2.1代碼略。。。。。。

    7.2.2授權服務器配置

    1)@EnableAuthorizationServer 注解

    @EnableAuthorizationServer 注解用來注釋 AuthorizationServerConfigurerAdapter的繼承類配置OAuth2.0 授權服務器的參數。

    AuthorizationServerConfigurerAdapter要求配置以下幾個類,這幾個類是由Spring創建的獨立的配置對象,

    它們會被Spring傳入AuthorizationServerConfigurer中進行配置。

    public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer { public AuthorizationServerConfigurerAdapter() {} public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {}public void configure(ClientDetailsServiceConfigurer clients) throws Exception {}public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {} }

    ClientDetailsServiceConfifigurer:用來配置客戶端詳情服務(ClientDetailsService),客戶端詳情信息在 這里進行初始化,你能夠把客戶端詳情信息寫死在這里或者是通過數據庫來存儲調取詳情信息。

    AuthorizationServerEndpointsConfifigurer:用來配置令牌(token)的訪問端點和令牌服務(token services)。

    AuthorizationServerSecurityConfifigurer:用來配置令牌端點的安全約束.

    2)配置客戶端詳細信息

    ClientDetailsServiceConfigurer 能夠使用內存或者JDBC來實現客戶端詳情服務(ClientDetailsService),

    ClientDetailsService負責查找ClientDetails,而ClientDetails有幾個重要的屬性如下列表:

    clientId:(必須的)用來標識客戶的Id。

    secret:(需要值得信任的客戶端)客戶端安全碼,如果有的話。

    scope:用來限制客戶端的訪問范圍,如果為空(默認)的話,那么客戶端擁有全部的訪問范圍。

    authorizedGrantTypes:此客戶端可以使用的授權類型,默認為空。

    authorities:此客戶端可以使用的權限(基于Spring Security authorities)。

    客戶端詳情(Client Details)能夠在應用程序運行的時候進行更新,可以通過訪問底層的存儲服務(例如將客戶 端詳情存儲在一個關系數據庫的表中,

    就可以使用 JdbcClientDetailsService)或者通過自己實現 ClientRegistrationService接口(同時你也可以實現 ClientDetailsService 口)來進行管理。

    3)管理令牌服務

    AuthorizationServerTokenServices 接口定義了一些操作使得你可以對令牌進行一些必要的管理,令牌可以被用來加載身份信息,里面包含了這個令牌的相關權限。

    自己可以創建 AuthorizationServerTokenServices 這個接口的實現,則需要繼承 DefaultTokenServices 這個類, 里面包含了一些有用實現,你可以使用它來修改令牌的格式和令牌的存儲。默認的,當它嘗試創建一個令牌的時候,是使用隨機值來進行填充的,除了持久化令牌是委托一個 TokenStore 接口來實現以外,這個類幾乎幫你做了 所有的事情。并且 TokenStore 這個接口有一個默認的實現,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了內存中。除了使用這個類以外,你還可以使用一些其他的預定義實現,下面有幾個版本,它們都 實現了TokenStore接口:

    **InMemoryTokenStore:**這個版本的實現是被默認采用的,它可以完美的工作在單服務器上(即訪問并發量 壓力不大的情況下,并且它在失敗的時候不會進行備份),大多數的項目都可以使用這個版本的實現來進行 嘗試,你可以在開發的時候使用它來進行管理,因為不會被保存到磁盤中,所以更易于調試。

    **JdbcTokenStore:**這是一個基于JDBC的實現版本,令牌會被保存進關系型數據庫。使用這個版本的實現時,你可以在不同的服務器之間共享令牌信息,使用這個版本的時候請注意把"spring-jdbc"這個依賴加入到你的 classpath當中。

    **JwtTokenStore:**這個版本的全稱是 JSON Web Token(JWT),它可以把令牌相關的數據進行編碼(因此對 于后端服務來說,它不需要進行存儲,這將是一個重大優勢),但是它有一個缺點,那就是撤銷一個已經授權令牌將會非常困難,所以它通常用來處理一個生命周期較短的令牌以及撤銷刷新令牌(refresh_token)。 另外一個缺點就是這個令牌占用的空間會比較大,如果你加入了比較多用戶憑證息。JwtTokenStore 不會保存任何數據,但是它在轉換令牌值以及授權信息方面與 DefaultTokenServices 所扮演的角色是一樣的。

    4)令牌訪問端點配置

    AuthorizationServerEndpointsConfifigurer 這個對象的實例可以完成令牌服務以及令牌endpoint配置。

    配置授權類型(Grant Types),AuthorizationServerEndpointsConfifigurer 通過設定以下屬性決定支持的授權類型(Grant Types):

    authenticationManager:

    認證管理器,當你選擇了資源所有者密碼(password)授權類型的時候,請設置這個屬性注入一個AuthenticationManager 對象。

    userDetailsService:

    如果你設置了這個屬性的話,那說明你有一個自己的 UserDetailsService 接口的實現, 或者你可以把這個東西設置到全局域上面去(例如 GlobalAuthenticationManagerConfifigurer 這個配置對象),當你設置了這個之后,那么 “refresh_token” 即刷新令牌授權類型模式的流程中就會包含一個檢查,用來確保這個賬號是否仍然有效,假如說你禁用了這個賬戶的話。

    authorizationCodeServices:

    這個屬性是用來設置授權碼服務的(AuthorizationCodeServices 的實例對象),主要用于 “authorization_code” 授權碼類型模式。

    implicitGrantService:

    這個屬性用于設置隱式授權模式,用來管理隱式授權模式的狀態。

    tokenGranter:

    當你設置了這個東西(即 TokenGranter 接口實現),那么授權將會交由你來完全掌控,并且會忽略掉上面的這幾個屬性,這個屬性一般是用作拓展用途的,即標準的四種授權模式已經滿足不了你的需求的時候,才會考慮使用這個。

    5)配置授權端點的URL(Endpoint URLs)

    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應該被Spring Security保護起來只供授權用戶訪問.

    6)令牌端點的安全約束

    AuthorizationServerSecurityConfifigurer:用來配置令牌端點(Token Endpoint)的安全約束,在

    AuthorizationServer中配置如下

    @Override public void configure(AuthorizationServerSecurityConfigurer security){ security .tokenKeyAccess("permitAll()") (1) // tokenkey這個endpoint當使用JwtToken且使用非對稱加密時,資源服務用于獲取公鑰而開放的,這里指這個endpoint完全公開。 .checkTokenAccess("permitAll()") (2) // checkToken這個endpoint完全公開 .allowFormAuthenticationForClients() (3) // 允許表單認證 ; }

    授權服務配置總結:授權服務配置分成三大塊,可以關聯記憶。

    既然要完成認證,它首先得知道客戶端信息從哪兒讀取,因此要進行客戶端詳情配置。

    既然要頒發token,那必須得定義token的相關endpoint,以及token如何存取,以及客戶端支持哪些類型的 token。

    既然暴露除了一些endpoint,那對這些endpoint可以定義一些安全上的約束等

    7.2.3.授權碼模式

    1)資源擁有者打開客戶端,客戶端要求資源擁有者給予授權,它將瀏覽器被重定向到授權服務器,

    ? 重定向時會附加客戶端的身份信息。如:

    /uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com

    參數列表如下:

    client_id:客戶端準入標識。

    response_type:授權碼模式固定為code。

    scope:客戶端權限。

    redirect_uri:跳轉uri,當授權碼申請成功后會跳轉到此地址,并在后邊帶上code參數(授權碼)。

    2)瀏覽器出現向授權服務器授權頁面,之后將用戶同意授權。

    3)授權服務器將授權碼(AuthorizationCode)轉經瀏覽器發送給client(通過redirect_uri)。

    4)客戶端拿著授權碼向授權服務器索要訪問access_token,請求如下:

    /uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://w ww.baidu.com

    參數列表如下

    client_id:客戶端準入標識。

    client_secret:客戶端秘鑰。

    grant_type:授權類型,填寫authorization_code,表示授權碼模式

    code:授權碼,就是剛剛獲取的授權碼,注意:授權碼只使用一次就無效了,需要重新申請。

    redirect_uri:申請授權碼時的跳轉url,一定和申請授權碼時用的redirect_uri一致。

    5)授權服務器返回令牌(access_token)

    這種模式是四種模式中最安全的一種模式。一般用于client是Web服務器端應用或第三方的原生App調用資源服務 的時候。因為在這種模式中access_token不會經過瀏覽器或移動端的App,而是直接從服務端去交換,這樣就最大 限度的減小了令牌泄漏的風險。

    7.2.4.簡化模式

    1)資源擁有者打開客戶端,客戶端要求資源擁有者給予授權,它將瀏覽器被重定向到授權服務器,

    ? 重定向時會附加客戶端的身份信息。如:

    /uaa/oauth/authorize?client_id=c1&response_type=code&scope=all&redirect_uri=http://www.baidu.com

    參數描述同授權碼模式 ,注意response_type=token,說明是簡化模式。

    2)瀏覽器出現向授權服務器授權頁面,之后將用戶同意授權。

    3)授權服務器將授權碼將令牌(access_token)以Hash的形式存放在重定向uri的fargment中發送給瀏覽器。

    **注:**fragment 主要是用來標識 URI 所標識資源里的某個資源,在 URI 的末尾通過 (#)作為 fragment 的開頭, 其中 # 不屬于 fragment 的值。如https://domain/index#L18這個 URI 中 L18 就是 fragment 的值。只需要知道js通過響應瀏覽器地址欄變化的方式能獲取到fragment 就行了。

    一般來說,簡化模式用于沒有服務器端的第三方單頁面應用,因為沒有服務器端就無法接收授權碼。

    7.2.5.密碼模式

    1)資源擁有者將用戶名、密碼發送給客戶端

    2)客戶端拿著資源擁有者的用戶名、密碼向授權服務器請求令牌(access_token),請求如下:

    /uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=password&username=shangsan&password=123

    參數列表如下:

    client_id:客戶端準入標識。

    client_secret:客戶端秘鑰。

    grant_type:授權類型,填寫password表示密碼模式

    username:資源擁有者用戶名。

    password:資源擁有者密碼。

    3)授權服務器將令牌(access_token)發送給client

    這種模式十分簡單,但是卻意味著直接將用戶敏感信息泄漏給了client,因此這就說明這種模式只能用于client是我們自己開發的情況下。因此密碼模式一般用于我們自己開發的,第一方原生App或第一方單頁面應用。

    7.2.6.客戶端模式

    1)客戶端向授權服務器發送自己的身份信息,并請求令牌(access_token)

    2)確認客戶端身份無誤后,將令牌(access_token)發送給client,請求如下:

    /uaa/oauth/token?client_id=c1&client_secret=secret&grant_type=client_credentials

    參數列表如下:

    client_id:客戶端準入標識。

    client_secret:客戶端秘鑰。

    grant_type:授權類型,填寫client_credentials表示客戶端模式

    這種模式是最方便但最不安全的模式。因此這就要求我們對client完全的信任,而client本身也是安全的。因此這種模式一般用來提供給我們完全信任的服務器端服務。比如,合作方系統對接,拉取一組用戶信息。

    7.2.7.資源服務測試

    資源服務器配置

    @EnableResourceServer 注解到一個 @Confifiguration 配置類上,

    并且必須使用 ResourceServerConfigurer 這個配置對象來進行配置(可以選擇繼承自 ResourceServerConfifigurerAdapter 然后覆寫其中的方法,參數就是這個 對象的實例),下面是一些可以配置的屬性:

    ResourceServerSecurityConfifigurer中主要包括:

    tokenServices:ResourceServerTokenServices 類的實例,用來實現令牌服務。

    tokenStore:TokenStore類的實例,指定令牌如何訪問,與tokenServices配置可選

    resourceId:這個資源服務的ID,這個屬性是可選的,但是推薦設置并在授權服務中進行驗證。 其他的拓展屬性例如 tokenExtractor 令牌提取器用來提取請求中的令牌。

    HttpSecurity配置這個與Spring Security類似:

    請求匹配器,用來設置需要進行保護的資源路徑,默認的情況下是保護資源服務的全部路徑。

    ? 通過http.authorizeRequests()來設置受保護資源的訪問規則

    ? 其他的自定義權限保護規則通過 HttpSecurity 來進行配置。

    @EnableResourceServer 注解自動增加了一個類型為 OAuth2AuthenticationProcessingFilter 的過濾器鏈

    編寫ResouceServerConfig類 略。。。

    驗證token

    ResourceServerTokenServices 是組成授權服務的另一半,如果你的授權服務和資源服務在同一個應用程序上的話,你可以使用 DefaultTokenServices ,這樣的話,你就不用考慮關于實現所有必要的接口的一致性問題。如果你的資源服務器是分離開的,那么你就必須要確保能夠有匹配授權服務提供的 ResourceServerTokenServices,它 知道如何對令牌進行解碼。

    令牌解析方法:

    使用 DefaultTokenServices 在資源服務器本地配置令牌存儲、解碼、解析方式 使用RemoteTokenServices 資源服務器通過 HTTP 請求來解碼令牌,每次都請求授權服務器端點 /oauth/check_token使用授權服務的 /oauth/check_token 端點你需要在授權服務將這個端點暴露出去,以便資源服務可以進行訪問,

    7.3 JWT令牌

    通過上邊的測試我們發現,當資源服務和授權服務不在一起時資源服務使用RemoteTokenServices 遠程請求授權服務驗證token,

    如果訪問量較大將會影響系統的性能 。

    解決上邊問題:

    令牌采用JWT格式即可解決上邊的問題,用戶認證通過會得到一個JWT令牌,

    JWT令牌中已經包括了用戶相關的信 息,客戶端只需要攜帶JWT訪問資源服務,資源服務根據事先約定的算法自行完成令牌校驗,無需每次都請求認證服務完成授權。

    什么是JWT?

    JSON Web Token(JWT)是一個開放的行業標準(RFC 7519),它定義了一種簡介的、自包含的協議格式,用于 在通信雙方傳遞json對象,傳遞的信息經過數字簽名可以被驗證和信任。JWT可以使用HMAC算法或使用RSA的公 鑰/私鑰對來簽名,防止被篡改。

    官網:https://jwt.io/

    標準:https://tools.ietf.org/html/rfc7519

    JWT令牌的優點:

    1)jwt基于json,非常方便解析。

    2)可以在令牌中自定義豐富的內容,易擴展。

    3)通過非對稱加密算法及數字簽名技術,JWT防止篡改,安全性高。

    4)資源服務使用JWT可不依賴認證服務即可完成授權。

    缺點:

    1)JWT令牌較長,占存儲空間比較大。

    JWT令牌結構

    通過學習JWT令牌結構為自定義jwt令牌打好基礎。

    JWT令牌由三部分組成,每部分中間使用點 . 分隔,比如:xxxxx.yyyyy.zzzzz

    base64UrlEncode(header):jwt令牌的第一部分。

    base64UrlEncode(payload):jwt令牌的第二部分。

    secret:簽名所使用的密鑰。jwt令牌的第三部分 。

    Header

    頭部包括令牌的類型(即JWT)及使用的哈希算法(如HMAC SHA256或RSA)

    一個例子如下: 下邊是Header部分的內容

    { "alg": "HS256", "typ": "JWT" }

    將上邊的內容使用Base64Url編碼,得到一個字符串就是JWT令牌的第一部分

    Payload

    第二部分是負載,內容也是一個json對象,它是存放有效信息的地方,它可以存放jwt提供的現成字段,比 如:

    iss(簽發者) exp(過期時間戳) sub(面向的用戶)等,也可自定義字段。

    此部分不建議存放敏感信息,因為此部分可以解碼還原原始內容。

    最后將第二部分負載使用Base64Url編碼,得到一個字符串就是JWT令牌的第二部分。

    { "sub": "1234567890", "name": "456", "admin": true }

    Signature

    第三部分是簽名,此部分用于防止jwt內容被篡改。

    這個部分使用base64url將前兩部分進行編碼,編碼后使用點 . 連接組成字符串,最后使用header中聲明簽名算法進行簽名。

    HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

    8、Spring Security實現分布式系統授權

    d336a09ff60a21bcd2950960b34d999e

    需求分析

    1、UAA認證服務負責認證授權。

    2、所有請求經過 網關到達微服務

    3、網關負責鑒權客戶端以及請求轉發

    4、網關將token解析后傳給微服務,微服務進行授權。

    所有微服務的請求都經過網關,網關從注冊中心讀取微服務的地址,將請求轉發至微服務。

    網關

    網關整合 OAuth2.0 有兩種思路,一種是認證服務器生成jwt令牌, 所有請求統一在網關層驗證,判斷權限等操作;

    另一種是由各資源服務處理,網關只做請求轉發。

    我們選用第一種。我們把API網關作為OAuth2.0的資源服務器角色,實現接入客戶端權限攔截、令牌解析并轉發當

    前登錄用戶信息(jsonToken)給微服務,這樣下游微服務就不需要關心令牌格式解析以及OAuth2.0相關機制了。

    API網關在認證授權體系里主要負責兩件事:

    (1)作為OAuth2.0的資源服務器角色,實現接入方權限攔截。

    (2)令牌解析并轉發當前登錄用戶信息(明文token)給微服務

    微服務拿到明文token(明文token中包含登錄用戶的身份和權限信息)后也需要做兩件事:

    (1)用戶授權攔截(看當前用戶是否有權訪問該資源)

    (2)將用戶信息存儲進當前線程上下文(有利于后續業務邏輯隨時獲取當前用戶信息)

    其他代碼略。。。。。。。。

    總結

    以上是生活随笔為你收集整理的Spring Security OAuth2.0认证授权的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    国产精品欧美 | 久久看片网站 | 天天操天天拍 | 天天操天天拍 | 99爱国产精品 | 在线观看91av | 成人午夜精品久久久久久久3d | 黄色软件视频网站 | 成人av片免费观看app下载 | 亚洲人人网 | 国产美女精品在线 | 国产精品麻豆91 | 夜夜躁日日躁狠狠久久av | 麻豆国产网站入口 | 99久久久久久久久 | 在线播放视频一区 | 国产黄在线看 | 中文日韩在线 | 国产午夜亚洲精品 | 日韩免费一区二区三区 | 在线观影网站 | www天天操 | 91久久精品日日躁夜夜躁国产 | 在线观看中文字幕 | 久久久久五月天 | 久草在线免费色站 | 日韩中文字幕免费视频 | 一区二区三区 亚洲 | 国内精品久久久久久久影视麻豆 | 久久高视频 | 在线观看中文字幕一区 | 国产色在线 | 91精品久久久久久久久久久久久 | 久久久久亚洲最大xxxx | 亚洲永久在线 | 久久久高清免费视频 | a在线观看国产 | 五月婷婷激情综合网 | 亚洲精品tv久久久久久久久久 | 国产精品999久久久 久产久精国产品 | 中文字幕在线视频精品 | 国产精品久久电影观看 | 一级淫片a| 日韩在线观看高清 | 成人免费在线电影 | 久久九九网站 | 综合在线色 | 91精品专区 | 国产免费国产 | 国产精品免费在线观看视频 | 国产精品欧美久久久久久 | 狠狠色免费| 中文字幕免费看 | 国产精品永久久久久久久www | 国产精品久久久久久久午夜片 | 久久99国产精品免费网站 | 中文字幕在线免费 | 99欧美精品 | 欧美国产日韩在线视频 | 99精品免费视频 | av网站免费线看精品 | 免费一级黄色 | 久草免费福利在线观看 | 草免费视频 | 日韩在线中文字幕 | 伊人成人久久 | 青草视频在线 | 天天射天天色天天干 | 97夜夜澡人人双人人人喊 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 国产精品永久免费 | 亚洲免费资源 | 99情趣网视频 | 国产999精品久久久影片官网 | 国产精品久久久久久久久久尿 | 国产日韩精品一区二区三区在线 | www.天天干 | 最新高清无码专区 | 欧美色伊人 | av在线免费在线观看 | 91日韩精品视频 | 欧美日韩国产在线一区 | 国产一区二区三区视频在线 | 亚洲情感电影大片 | 在线免费观看视频你懂的 | 国产精品麻豆99久久久久久 | 美女免费av| 日日干夜夜操视频 | 久久久久久久国产精品 | 亚洲国产中文在线 | 黄色在线观看免费 | 91热视频 | 免费视频91蜜桃 | 亚洲成av人片在线观看 | www.天天操 | 国产裸体视频bbbbb | 亚洲一级久久 | 伊香蕉大综综综合久久啪 | av片一区 | 青青河边草免费观看 | 亚洲人成免费网站 | 欧美国产高清 | 韩国在线视频一区 | 9999国产| 国产综合久久 | 中文字幕在线视频免费播放 | 在线观看av国产 | av高清免费| 日韩v在线| 二区三区av| 国产精品区一区 | 久久精品—区二区三区 | 欧美成人黄 | 国产精品免费观看久久 | 色91在线| 精品视频久久 | 欧美疯狂性受xxxxx另类 | 久久图 | 亚洲视频 中文字幕 | 99视频在线精品 | 免费一区在线 | 一级淫片在线观看 | 六月天综合网 | 日韩视频中文字幕在线观看 | 日韩免费一区二区 | 国产亚洲精品女人久久久久久 | 国产精品一区二区吃奶在线观看 | 国精产品满18岁在线 | 亚州国产精品久久久 | 91免费高清视频 | 日韩精品高清视频 | 国产麻豆视频免费观看 | 国产精品入口传媒 | 在线观看久久 | 狠狠的操 | av中文字幕不卡 | 国产精品18毛片一区二区 | 黄色软件在线观看 | 91精品导航 | av大片网站 | 日日干 天天干 | 久草在线免费新视频 | 一级黄色在线免费观看 | 国产免费观看久久黄 | 久久婷婷国产色一区二区三区 | 99视频在线观看免费 | 欧美激情xxxx | 亚洲日本va午夜在线影院 | 少妇视频一区 | 99福利影院| 色婷婷成人网 | 国产精品色视频 | 美女网站色在线观看 | 国产精品18videosex性欧美 | 91桃色免费观看 | 午夜av大片 | 国产中文在线视频 | 日韩中文字幕视频在线观看 | 成人免费视频观看 | 国内外成人免费在线视频 | 天天干夜夜夜 | 日批视频在线观看免费 | 欧美aa在线观看 | www.夜夜操 | 开心激情久久 | 久青草电影 | 亚洲精品在线播放视频 | 狠狠干网 | 日韩视频一区二区三区在线播放免费观看 | 日韩女同一区二区三区在线观看 | 国产精品一区在线观看 | 一区三区视频在线观看 | 婷婷在线免费 | 五月天婷婷免费视频 | 日韩美女av在线 | 日日弄天天弄美女bbbb | 国产精品情侣视频 | 国产一性一爱一乱一交 | 久草在在线视频 | 黄色av电影一级片 | 激情久久网 | 欧美视频网址 | 日韩av不卡在线 | 成人av一级片 | 免费一级特黄毛大片 | av中文在线 | 国产精品国内免费一区二区三区 | 人人干免费| 日韩福利在线观看 | 人人爽人人干 | 免费一级片在线 | 亚洲最新毛片 | 国产香蕉久久精品综合网 | 亚洲第一区精品 | 亚洲精品一区二区精华 | 久久手机精品视频 | 色999视频| 最新中文字幕在线资源 | 91av蜜桃 | 中文字幕丝袜美腿 | 国产精品久久久久久久久久久免费 | 最新色视频 | 精品国产精品国产偷麻豆 | 美女黄久久 | 久久激情婷婷 | 怡红院av久久久久久久 | 色www免费视频 | 中文字幕在线视频第一页 | 国产精品久久久久永久免费观看 | 亚洲欧美日韩国产精品一区午夜 | 在线看一区二区 | 99精品免费在线观看 | 香蕉视频亚洲 | 黄网站a | 亚洲 成人 一区 | av夜夜操 | 日本黄色大片免费看 | www.伊人网| 亚洲国产字幕 | 久久精品中文字幕免费mv | 国产免费二区 | 激情欧美一区二区三区 | 久精品视频免费观看2 | 九九热视频在线免费观看 | www.久久免费 | 婷婷激情5月天 | 亚洲无线视频 | 在线播放 日韩专区 | 91亚洲精品久久久蜜桃 | 久久九九国产精品 | 国产日韩精品一区二区在线观看播放 | 人人草在线视频 | 亚洲成人黄 | 91在线看黄 | 亚洲专区路线二 | 奇米网在线观看 | 午夜电影 电影 | 久久久久免费精品国产 | 天天艹天天 | 国产成人av一区二区三区在线观看 | 欧美成人精品欧美一级乱 | 日韩高清精品免费观看 | 国产精品福利在线观看 | 精品一区二区免费视频 | 久久成熟| 一区二区三区四区在线 | 久草视频中文在线 | 91看毛片 | 国产视频丨精品|在线观看 国产精品久久久久久久久久久久午夜 | 国产亚洲视频在线 | 日韩高清在线一区二区 | 91一区二区三区久久久久国产乱 | 在线视频麻豆 | 亚洲尺码电影av久久 | 开心综合网 | 在线看中文字幕 | 不卡的一区二区三区 | 毛片永久新网址首页 | 久久天天躁狠狠躁亚洲综合公司 | 日韩特黄一级欧美毛片特黄 | 高潮毛片无遮挡高清免费 | 日日夜夜精品免费视频 | 91成人精品观看 | 国产蜜臀av| 97超级碰 | 999超碰 | 在线精品视频在线观看高清 | 天天色天天综合网 | 九九热99视频| 欧美日韩另类在线观看 | 国产日韩欧美在线影视 | 免费性网站 | 97偷拍在线视频 | 四虎国产精品成人免费影视 | 欧美大香线蕉线伊人久久 | 天天综合在线观看 | 精品在线一区二区 | 在线播放一区二区三区 | 国产成人亚洲在线观看 | 午夜色影院 | 亚州精品国产 | 国产成人61精品免费看片 | 人人射| 黄色av电影免费观看 | 国产精品理论视频 | 国产亚洲成人网 | 亚洲一区黄色 | 天天综合久久 | 中文字幕在线播放日韩 | 中文字幕免费高清av | 久久黄色片子 | 中文字幕一区二区三区精华液 | 亚洲 中文 欧美 日韩vr 在线 | 九色精品免费永久在线 | 亚洲一区二区视频在线播放 | 爱爱av网站 | 草久在线 | 人操人| 天天色天天| 91夫妻自拍| 免费视频在线观看网站 | 国产精品99久久久久久人免费 | 五月亚洲综合 | 国产精品精品国产色婷婷 | 日韩艹 | 欧洲性视频 | 99精品国产高清在线观看 | 中文字幕久久精品 | 国产精品一区二区久久国产 | 午夜色影院| 黄色免费观看视频 | 精品九九九九 | 中文字幕在线观看完整 | 欧美日韩午夜爽爽 | 97超碰资源 | 午夜影院一级片 | 新版资源中文在线观看 | 亚州欧美视频 | 欧美一级片在线观看视频 | 伊人六月 | 波多野结衣在线观看视频 | 久久精品这里都是精品 | 国产日韩欧美视频 | 人人爱人人舔 | 国产精品久久久视频 | 久久尤物电影视频在线观看 | 韩日成人av | 一区二区欧美在线观看 | 亚洲高清在线精品 | 日韩综合在线观看 | 激情av网 | 久久久久久久久电影 | 狠狠操电影网 | 一级欧美一级日韩 | 日韩欧美国产精品 | 国产成人99av超碰超爽 | 蜜臀av性久久久久av蜜臀妖精 | 西西444www高清大胆 | 亚洲精品在线视频观看 | 欧美最猛性xxxxx免费 | 国产中的精品av小宝探花 | 欧美日韩中文国产 | 日本高清久久久 | 日韩在线免费看 | 98超碰在线| www.天天成人国产电影 | 三上悠亚一区二区在线观看 | 成人免费观看完整版电影 | 在线 日韩 av | 日韩理论在线视频 | 激情综合五月天 | 欧美日韩国产精品一区二区三区 | 免费久久久久久久 | 高清精品久久 | 国产一区二区三区 在线 | av黄色影院 | 免费看国产精品 | 久久成人黄色 | 国产成人三级三级三级97 | av电影一区二区三区 | 久久人人爽视频 | 久久免费视频一区 | 欧美日韩在线观看一区二区三区 | 国产精品一区二区三区免费视频 | 五月天.com| 日韩欧美亚州 | 中文字幕一区二区三区精华液 | 激情久久伊人 | 欧美日韩中文字幕综合视频 | 天天干人人 | 亚洲精品女人久久久 | 午夜av免费| 狠狠操狠狠干天天操 | 黄色片视频免费 | 免费视频久久久久 | 麻花豆传媒mv在线观看 | 97自拍超碰| h网站免费在线观看 | 亚洲视屏 | 一级α片免费看 | 夜夜躁狠狠躁日日躁视频黑人 | 在线天堂中文www视软件 | 夜夜视频| 国产精品第 | 精品一区三区 | av网站免费在线 | 色网av| 综合精品久久久 | 美女在线观看网站 | 欧美伦理电影一区二区 | 国产精品青草综合久久久久99 | 亚洲国产免费网站 | 超碰av在线播放 | 国产精品黑丝在线观看 | 香蕉97视频观看在线观看 | 三上悠亚一区二区在线观看 | 亚洲在线视频免费观看 | 色a4yy| 久久久精品国产免费观看同学 | 亚洲精品成人网 | 久久午夜网 | 欧美日韩视频一区二区三区 | 欧美一级特黄aaaaaa大片在线观看 | 91福利专区 | 麻花豆传媒一二三产区 | japanesexxxhd奶水| 国产精品免费在线播放 | av在线免费观看不卡 | 91九色成人 | 97在线观看免费高清 | 亚洲午夜精品久久久 | 成人a在线观看高清电影 | 久久精品电影院 | 婷婷六月天综合 | 丰满少妇高潮在线观看 | 五月婷亚洲 | 免费进去里的视频 | 亚洲欧美精品在线 | 一区二区欧美日韩 | 国产精品视频观看 | 亚洲国产97在线精品一区 | 日本最新一区二区三区 | 96久久欧美麻豆网站 | 欧美福利片在线观看 | 中文字幕免费高清 | 国产午夜剧场 | 中文字字幕在线 | 精品福利网 | 国产视频亚洲视频 | 久久成人18免费网站 | 国产精品99久久免费黑人 | 久久精品中文字幕免费mv | 久久久午夜电影 | 操高跟美女 | 少妇高潮流白浆在线观看 | 日本精品视频免费 | 国产精品久久久久久妇 | 97福利| av黄色av| 日韩欧美一区二区三区在线观看 | www.五月天激情 | 91精品在线麻豆 | 久久久国产一区二区三区 | 97超碰资源 | 狠狠干夜夜操 | 在线播放你懂 | 色九色 | 91久久人澡人人添人人爽欧美 | 国外调教视频网站 | 欧美一区,二区 | 中文字幕一区二区三区久久蜜桃 | 亚洲日本va午夜在线影院 | 久操视频在线 | 国内精品久久久精品电影院 | 一区二区三区高清 | 久久久污| 国产高清精品在线 | 最新动作电影 | 欧美极品xxxx| 福利在线看片 | av一级在线观看 | 国产伦精品一区二区三区… | 美女精品在线观看 | 亚在线播放中文视频 | 亚洲婷婷免费 | 99热最新在线 | 中文字幕av电影下载 | 色综合五月天 | 国内精品亚洲 | 超碰在线9| 人人爱人人射 | 成人欧美一区二区三区黑人麻豆 | 久久精品视频2 | 久99视频| 96国产精品视频 | 国产麻豆果冻传媒在线观看 | 伊人天天狠天天添日日拍 | 91久久奴性调教 | 国产亚洲精品久久久久久 | 亚洲一区日韩 | 九九热1| 午夜精品视频一区 | 成人国产精品一区二区 | 丁香5月婷婷久久 | 久久资源在线 | 在线视频一区观看 | 国产视频69 | 欧美精品久久久久久久久久丰满 | 中文字幕第一 | 免费亚洲视频在线观看 | 国产视频一区在线播放 | 天天综合网 天天综合色 | 国产精品正在播放 | 欧美午夜性生活 | 国产一级视频 | 美女黄频网站 | 久久国产网 | av中文在线影视 | 亚洲最新合集 | 久久爱www. | 国产精品成人a免费观看 | www.成人精品 | 成人av在线网| 婷婷色网视频在线播放 | 4hu视频| 免费看v片网站 | av电影久久| 欧美狠狠操 | 国产精品第52页 | 欧美日韩高清在线 | 久久亚洲综合色 | 国产精品久久久久久久久久久久午夜 | 国产精品免费久久 | 日韩黄色免费看 | 91激情视频在线播放 | 精品国产乱码久久久久久三级人 | 国产一级免费在线观看 | 中文av资源站| 99re国产| 国产在线视频在线观看 | 日韩免费区 | 天天天操操操 | 色综合天天综合 | 欧美xxxxx在线视频 | 2023天天干 | 99国产精品一区 | 欧美精品乱码久久久久 | 中文字幕有码在线观看 | 日韩精品在线播放 | 999久久久免费精品国产 | 国产精品18毛片一区二区 | 又黄又爽又色无遮挡免费 | 成人动漫视频在线 | 狠狠狠色丁香婷婷综合久久五月 | 久久狠狠亚洲综合 | 国产成人福利在线观看 | 国产精品视频大全 | 日日狠狠 | 国产精品免费视频一区二区 | 久久99久久久久久 | 99国产成+人+综合+亚洲 欧美 | 欧美精彩视频在线观看 | 天天射天天爱天天干 | 色婷婷成人网 | 麻豆国产精品永久免费视频 | 99在线看 | 国产高清视频在线 | 国产精品美女久久久久久 | www蜜桃视频 | 久久国产欧美日韩精品 | 丁香婷婷基地 | 天天综合网天天综合色 | 欧美做受xxx| 在线一二区 | 久久久久久久影视 | 国产一区二区在线免费视频 | 狠狠综合久久av | 韩国av免费看 | 国产色爽| 深夜福利视频一区二区 | 日本深夜福利视频 | 成人免费观看网站 | 久久久久久久福利 | 97色婷婷成人综合在线观看 | 在线免费观看国产 | 日本激情视频中文字幕 | 免费在线观看国产精品 | 久久久亚洲成人 | 亚洲精品国偷自产在线99热 | 免费又黄又爽 | 久久久久国产一区二区 | 久久黄色美女 | 一级片免费视频 | 精品在线观看国产 | 日韩精品一区二区三区免费观看 | 一区二区三区动漫 | 国产区精品视频 | 欧美日本一二三 | 国产精品久久久久av福利动漫 | 天天干夜夜夜操天 | 久久激情电影 | 国产在线永久 | 国产黄色精品在线观看 | 中文字幕中文字幕 | 三级黄色三级 | 久久久综合色 | 五月婷网| 久久久蜜桃一区二区 | 日韩欧美一级二级 | 久久6精品 | 亚洲欧美综合精品久久成人 | 久久精品久久国产 | 韩日精品在线观看 | 免费又黄又爽 | 激情综合电影网 | 综合国产在线 | 亚洲高清视频一区二区三区 | 国产在线国偷精品产拍免费yy | 中文字幕网址 | 亚洲第一色 | 亚洲欧美日韩精品久久奇米一区 | 成人av手机在线 | 日韩高清黄色 | 日韩午夜三级 | 超碰97国产 | 91精品免费在线 | 97色综合 | www.久久久com | 最新午夜 | av大全在线播放 | 天天干天天操天天拍 | 综合在线色 | 色婷婷免费| av在线播放快速免费阴 | 欧美一二三视频 | 久草免费在线 | 在线观看国产日韩欧美 | 日韩免费一二三区 | 中文字幕色婷婷在线视频 | 欧美日韩二区三区 | 久草资源在线观看 | 国产中文自拍 | 国产96在线 | 成人丁香花 | 免费看一级片 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 8090yy亚洲精品久久 | 天天艹天天操 | 色天堂在线视频 | 一区免费在线 | 亚洲激情 欧美激情 | 欧美一二区视频 | 人人舔人人干 | 久久99久久久久久 | 一级理论片在线观看 | 午夜日b视频| 久久97久久 | 日本韩国中文字幕 | 亚洲我射av | 97在线观看免费 | 97日日碰人人模人人澡分享吧 | 国内外成人免费在线视频 | 国产一级三级 | 国产一区二区免费在线观看 | 欧美久久综合 | 日韩高清精品免费观看 | 久久国产成人午夜av影院宅 | 日本中文乱码卡一卡二新区 | av夜夜操 | 亚洲国内精品在线 | 色噜噜在线观看 | 亚洲国内精品在线 | 国产一区久久久 | 国产69精品久久99的直播节目 | 91成人网在线播放 | 国产精品成人国产乱 | 久久欧美综合 | 98福利在线| 亚洲精品乱码白浆高清久久久久久 | 国内成人精品2018免费看 | 日韩成人中文字幕 | 婷婷色中文 | 免费99| 国产成本人视频在线观看 | 久久久999免费视频 日韩网站在线 | 亚洲a色 | 在线国产视频 | 美女精品| 九九视频网站 | 国产精品尤物 | 久久中文字幕导航 | 欧美日韩一区三区 | 国产精品 中文字幕 亚洲 欧美 | 日日碰狠狠躁久久躁综合网 | 韩国三级一区 | 日韩一级片观看 | 999国产在线 | 国产精品久久久久久一二三四五 | 国产一区二区三区免费在线 | 在线色吧| 黄色电影网站在线观看 | 午夜美女福利直播 | 精品一区二区三区四区在线 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 国产精品久久视频 | www.com久久久 | 亚洲国产网站 | 一二三区视频在线 | 亚欧洲精品视频在线观看 | 91资源在线观看 | 欧美日韩在线观看一区 | 国产精品久久久久亚洲影视 | 97在线观看视频免费 | 色偷偷88888欧美精品久久久 | 国产视频久久 | 六月丁香六月婷婷 | 国产成年人av | 黄色软件在线看 | 中文字幕一区二区三区乱码在线 | 成人av资源站 | 91精品国产麻豆国产自产影视 | 亚洲伦理中文字幕 | av不卡免费看| 久久99中文字幕 | 麻豆传媒一区二区 | 亚洲成人高清在线 | 91视频免费看 | 毛片网站观看 | 国产成人久久精品77777综合 | 国产一区二区三区在线免费观看 | 97人人模人人爽人人喊网 | 福利视频| 美女精品国产 | 久久久不卡影院 | 992tv人人网tv亚洲精品 | 超碰在线97免费 | 亚洲免费观看视频 | 黄色一区二区在线观看 | 大胆欧美gogo免费视频一二区 | 五月婷婷另类国产 | 日日夜夜精品免费观看 | 国产精品久久艹 | 国产亚洲精品v | 精品国产午夜 | 亚洲欧美视频一区二区三区 | 亚洲mv大片欧洲mv大片免费 | 国产一级片直播 | www.伊人网 | 免费看三级网站 | 午夜精品一区二区三区免费视频 | 午夜黄色大片 | 超碰日韩| 欧美精品999 | 成人电影毛片 | 天天天天爱天天躁 | 99视频免费 | 免费网站黄 | 久久婷婷精品视频 | 天天综合入口 | 色视频网站在线观看一=区 a视频免费在线观看 | 奇米导航 | 男女啪啪免费网站 | 毛片无卡免费无播放器 | 国产精品久久久久久久久久白浆 | 久久免费高清 | 久久久一本精品99久久精品 | 久久涩视频 | 人人草天天草 | 国产在线一卡 | 日韩一级成人av | 99久久精品久久久久久动态片 | 亚洲精品乱码久久久久久写真 | 日韩有色 | 天堂在线免费视频 | 免费观看特级毛片 | 91高清一区 | 国产专区欧美专区 | 天天综合成人 | av专区在线 | 美女免费黄网站 | 91av在线电影 | 久久99亚洲精品 | 欧美色综合天天久久综合精品 | 国产精品av久久久久久无 | 激情综合色图 | 久久人人爽人人爽人人片 | 黄色大片入口 | 人人澡人人爽欧一区 | 免费黄色av | 欧美日韩在线观看一区二区 | 国产午夜精品一区二区三区四区 | 婷婷丁香六月 | 日韩在线高清免费视频 | 久久欧洲视频 | 黄色高清视频在线观看 | 久久久免费视频播放 | 性日韩欧美在线视频 | 九九99 | 91精品国产一区 | 在线观看精品一区 | 日本午夜在线观看 | 插插插色综合 | 黄色大片免费网站 | 日韩精品视频免费专区在线播放 | 丁香高清视频在线看看 | 国产高清综合 | 亚洲精品理论片 | av在线收看 | 808电影| 日韩mv欧美mv国产精品 | 久久久久久精 | 久久久免费看视频 | 九色琪琪久久综合网天天 | 久久久av免费 | 丁香花在线观看视频在线 | 婷婷丁香综合 | 97电影在线看视频 | 日韩精品久久久久久久电影99爱 | 2021国产精品 | 欧美一二三区在线观看 | 久久精品精品电影网 | 国产黄色播放 | 黄色动态图xx | 国产91精品在线观看 | 玖草在线观看 | av三级在线播放 | 精品欧美一区二区在线观看 | 人人爱在线视频 | 久久久久久久久久久成人 | 97超碰国产精品女人人人爽 | 91资源在线| 波多野结衣视频一区二区三区 | 五月婷婷开心 | 免费国产一区二区视频 | 狠狠做六月爱婷婷综合aⅴ 日本高清免费中文字幕 | 亚洲高清视频一区二区三区 | 久久久久久久综合色一本 | 狠狠天天 | 看片网站黄 | 亚洲欧美日韩国产一区二区 | 精品免费久久久久 | 伊人狠狠色丁香婷婷综合 | 91av在线视频播放 | 亚洲国产精品va在线看黑人 | 亚洲欧美视频在线 | 婷婷综合在线 | 天天操天天摸天天干 | 一区二区视频在线免费观看 | 中文字幕av日韩 | 99久久这里有精品 | 国产精品一区二区在线观看 | 久久爱www. | 免费看污片 | 99精品视频免费观看视频 | 天天干夜夜 | 国产精品剧情 | 国产一二区免费视频 | 天天综合成人网 | 中文字幕文字幕一区二区 | 日韩高清 一区 | 日本在线免费看 | 久久久久久久久久久综合 | 国产主播大尺度精品福利免费 | 欧洲亚洲精品 | 精品视频国产 | 成人在线播放av | 欧美性生活大片 | 久久精品老司机 | 五月天婷婷免费视频 | 奇米网8888 | 亚洲精品乱码久久久久久 | 91香蕉国产在线观看软件 | 日韩中文在线播放 | 国产成人精品亚洲精品 | 欧日韩在线视频 | 香蕉视频免费看 | 麻豆综合网 | 激情在线免费视频 | 日韩高清dvd| 日本公妇在线观看 | 成年在线观看 | 日日操网 | 国产精品亚洲片夜色在线 | 亚洲国产成人精品在线观看 | 亚洲视频网站在线观看 | 欧美激情第一区 | 亚洲电影久久久 | 婷婷色社区 | 狠狠网站 | 99久久精品无码一区二区毛片 | 99在线观看 | 青青河边草观看完整版高清 | 久日视频 | 久久在线精品视频 | 国产精品久久电影网 | 免费91在线| 色视频在线观看免费 | av电影 一区二区 | 久久久久久综合网天天 | 免费看黄在线观看 | 色综合天天狠狠 | 狠狠干夜夜操 | 精品国产乱码一区二区三区在线 | 69xx视频 | av免费在线播放 | 中文av免费 | 国产精品高潮呻吟久久av无 | 麻豆传媒一区二区 | 性日韩欧美在线视频 | 一级黄视频 | 99re国产视频 | 婷婷丁香色 | 国产福利一区二区三区在线观看 | 国产精品久久久久久999 | 亚州精品在线视频 | 日批网站在线观看 | 狠狠色丁香九九婷婷综合五月 | 国产小视频免费在线观看 | 亚洲精选在线观看 | 国产日韩欧美在线一区 | 免费亚洲电影 | 97精品国产91久久久久久久 | 日本特黄特色aaa大片免费 | 韩国在线一区 | 久久视频精品在线 | 亚洲精品毛片一级91精品 | 片网站| 中文字幕欧美激情 | 狠狠干狠狠色 | 久99久视频 | 免费在线观看av电影 | 在线电影a | 日韩欧美在线观看一区二区三区 | 久久综合九色综合久久久精品综合 | 亚洲成人动漫在线观看 | 欧美与欧洲交xxxx免费观看 | 91精品国产自产在线观看永久 | 国产精品99久久久久的智能播放 | 伊人伊成久久人综合网小说 | 91精品视频导航 | 99在线观看精品 | 高清av中文在线字幕观看1 | 色就干| 国产日韩精品在线 | 超碰97中文 | 精品在线你懂的 | 在线观看国产一区 | 91成人久久 | 91精品视频在线观看免费 | 天天射综合| 亚洲精品中文字幕在线 | 亚洲日本一区二区在线 | 日批网站免费观看 | 日批网站在线观看 | 黄色国产在线 | 精品在线视频播放 | 久久国色夜色精品国产 | 中文字幕亚洲五码 | 久久国产精品99久久久久久老狼 | 又色又爽又黄 | 亚州精品天堂中文字幕 | 久久久久久国产精品999 | 久久 一区 | 99色网站| 亚洲精品高清一区二区三区四区 | 日韩av电影网站在线观看 | 91色在线观看视频 | 国产偷国产偷亚洲清高 | 亚洲一区二区三区在线看 | 综合精品久久 | 五月婷婷久草 | 精品福利网站 | 玖玖爱免费视频 | 国产资源免费在线观看 | 人人澡视频 | 成人一级在线 | 欧美日韩国产精品一区二区亚洲 | 日韩爱爱网站 | 国产精品都在这里 | 国产精品九九视频 | 精品主播网红福利资源观看 | 久久歪歪 | 日韩在线视频一区二区三区 | 97av在线视频免费播放 | 中文av字幕在线观看 | 国产精品久久久久久影院 | 国产乱码精品一区二区三区介绍 | 人人干人人草 | 婷婷久久婷婷 | 国产精品资源在线 | 久久精品国产免费看久久精品 | 久草网在线视频 | 在线观看免费av片 | 四虎国产精品免费观看视频优播 | 久久国产一区二区 | 久草视频资源 | 国产精品初高中精品久久 | 国产美女精品久久久 | 97在线观看免费高清 | 日本性高潮视频 | 日韩在线视频不卡 | 高清av影院 | 成 人 黄 色 免费播放 | 在线免费观看国产 | 九九九电影免费看 | 综合网色 | 91伊人久久大香线蕉蜜芽人口 | www.色婷婷 | 欧美激情va永久在线播放 | 国产成人精品一二三区 | 夜夜夜| www.99av | 精品视频久久 | 日韩大片在线观看 | 97看片网 | 国产福利在线免费 | 日韩视频www | 国产精品一码二码三码在线 |