天天看點

spring secutiry oauth2.0認證制授權 --Spring secuity快速上手

Spring Security快速上手

1Spring Security介紹

Spring Security是一個能夠為基于Spring 的企業應用系統提供聲明式的安全通路控制解決方案的安全架構,由于它是spring生态系統的一員,是以它伴随着整個spring生态系統不斷修正,更新,在spring boot項目中加入spring security更是十分簡單,使用spring security 減少了為企業系統安全控制編寫大量重複代碼的工作。

2建立工程

2.1建立maven工程

建立maven工程security spring security 

4.0.0com.wangjunji.securitysecurity-springmvc1.0-SNAPSHOTwarsecurity-springmvc Maven Webapphttp://www.example.comUTF-81.81.8org.springframeworkspring-webmvc5.1.5.RELEASEjavax.servletjavax.servlet-api3.0.1org.projectlomboklombok1.18.8junitjunit4.11testsecurity-springmvcmaven-clean-plugin3.1.0maven-resources-plugin3.0.2maven-compiler-plugin3.8.0maven-surefire-plugin2.22.1maven-war-plugin3.2.2maven-install-plugin2.5.2maven-deploy-plugin2.8.2      

2.2Spring容器配置

package com.wangjunji.security.springmvc.config;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(basePackages = "com.wangjunji.security.springmvc",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class ApplicationConfig {
    //在此配置除了controller的其它bean,比如:資料庫連結池,事務管理器,業務bean
}      

2.3Spring mvc context配置

package com.wangjunji.security.springmvc.config;

//import com.wangjunji.security.springmvc.interceptor.SimpleAuthticationInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.wangjunji.security.springmvc",
                includeFilters = {@ComponentScan.Filter(type= FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {
    //注入攔截器
    //@Autowired
    //SimpleAuthticationInterceptor simpleAuthticationInterceptor;

    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    //spring security提供的預設的視圖頁面
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/login");

    }




}      

首先實作認證功能

認證頁面

springSecurity預設提供認證頁面,不需要額外開發

安全配置

spring security提供了使用者名密碼登入,退出,會話管理等認證功能,隻需要配置即可使用

1)在config包下定義WebSecurityConfig,安全配置的内容包括:使用者資訊,密碼編輯器,安全攔截機制

package com.wangjunji.security.springmvc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //定義使用者資訊服務(查詢使用者資訊)
    @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;
    }
    //密碼編碼方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    //安全攔截機制 ----非常重要
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().
                antMatchers("/r/**").  //所有/r/**的請求必須通過認證通過
                authenticated().   //除了/r
                anyRequest().
                permitAll().
                and().formLogin().successForwardUrl("/login-success"); //自定義登陸成功的英面位址
    }
}      

Spring Security初始化

spring security初始化,這裡有兩種情況

若目前沒有使用spring 或spring mvc ,則需要将webSecurityConfig(Spring Security配置類)傳入超類,以確定擷取配置,并建立spring context

相反,若目前環境已經使用Spring,我們應該在現有的spring context中注冊spring security上一步已經将websecurityconfig加載到rootcontext,此方法可以什麼都不做

在init包下面定義

SpringSecurityApplicationInitializer :      
package com.wangjunji.security.springmvc.init;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SpringSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
    public SpringSecurityApplicationInitializer(){

    }

}      

配置controller

package com.wangjunji.security.springmvc.controller;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
public class LoginController {
    @GetMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
    public String rr1(HttpSession session){
            return   "通路/r/r1";
    }

    @GetMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
    public String rr2(HttpSession session){
            return   "通路/r/r2";
        }

    @RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
    public String loginSuccess(){
        return   "登陸成功";
    }

}      

實作鑒權

package com.wangjunji.security.springmvc.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //定義使用者資訊服務(查詢使用者資訊)
    @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;
    }
    //密碼編碼方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    //安全攔截機制 ----非常重要
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/r/r1").hasAuthority("p1")
                .antMatchers("/r/r2").hasAuthority("p2").
                antMatchers("/r/**").  //所有/r/**的請求必須通過認證通過
                authenticated().   //除了/r
                anyRequest().
                permitAll().
                and().formLogin().successForwardUrl("/login-success"); //自定義登陸成功的英面位址
    }
}      

小結

通過快速上手,spring security實作了認證和授權,spring security提供基于賬号和密碼的認證方式,通過安全配置就可實作請求攔截,授權功能,spring secutiy完成的不僅僅是這些。

Spring security應用詳解

內建spring boot 

spring boot介紹

spring boot是一套spring快速開發架構,基于Spring 4.0設計,spring boot開發可以避免一些繁瑣的工程搭建和配置,同時它內建大量的常用架構,快速導入依賴包的沖突。基本上常用的開發架構都支援spring boot開發,例如mybaits,dubbo等spring 家族更是如此,例如:spring cloud,spring mvc ,spring security 等,使用spring boot開發可以大大提高生産率,是以spring boot的使用率非常高。

本章講解如何通過spring boot開發spring security應用,spring boot提供spring boot starter security用于開發spring security應用

建立maven 工程

4.0.0
	
		org.springframework.boot
		spring-boot-starter-parent
		2.2.6.RELEASE
			
	com.wangjunji.security
	springbootsecurity
	0.0.1-SNAPSHOT
	springbootsecurity
	Demo project for Spring Boot

	
		1.8
	

	
		
			org.springframework.boot
			spring-boot-starter-security
		
		
			org.springframework.boot
			spring-boot-starter-web
		
		
			javax.servlet
			javax.servlet-api
			3.0.1
		


		
			org.springframework.boot
			spring-boot-devtools
			runtime
			true
		
		
			org.springframework.boot
			spring-boot-starter-test
			test
			
				
					org.junit.vintage
					junit-vintage-engine
				
			
		
		
			org.springframework.security
			spring-security-test
			test
		
	

	
		
			
				org.springframework.boot
				spring-boot-maven-plugin
			
		
	      

servlet context配置

由于spring boot starter 自動裝配機制,這裡無需使用@EnableWebMvc與@ComponentScan,WebConfig如下

package com.wangjunji.security.springbootsecurity.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {
    //spring security提供的預設的視圖頁面
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("redirect:/login");

    }

}      

視圖解頻器配置在application.propties中

spring.mvc.view.prefix=/WEB-INF/views
spring.mvc.view.suffix=.jsp      

配置安全配置

package com.wangjunji.security.springbootsecurity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //定義使用者資訊服務(查詢使用者資訊)
    @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;
    }
    //密碼編碼方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }
    //安全攔截機制 ----非常重要
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/r/r1").hasAuthority("p1")
                .antMatchers("/r/r2").hasAuthority("p2").
                antMatchers("/r/**").  //所有/r/**的請求必須通過認證通過
                authenticated().   //除了/r
                anyRequest().
                permitAll().
                and().formLogin().successForwardUrl("/login-success"); //自定義登陸成功的英面位址
    }
}      

建立controller,用于測試

package com.wangjunji.security.springbootsecurity;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpSession;

@RestController
public class LoginController {
    @GetMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
    public String rr1(HttpSession session){
        return   "通路/r/r1";
    }

    @GetMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
    public String rr2(HttpSession session){
        return   "通路/r/r2";
    }

    @RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
    public String loginSuccess(){
        return   "登陸成功";
    }

}      

啟動類

package com.wangjunji.security.springbootsecurity;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringbootsecurityApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringbootsecurityApplication.class, args);
	}

}      

Spring security 工作原理

結構總體構架

spring security 所解決的問題就是安全通路控制 ,而安全通路控制功能其實就是對所有進入系統的請求進行攔截,校驗每個請求是否能夠通路它所期望的資源,根據前知識的學習,通過filter或aop等技術來實作,spring secutiry對web資源的保護是靠filter實作的,所有從這個filter 來入手,逐漸深入spring security原理

當初始化spring securtiy入手,建立建一個springsecurityfilterchain的servlet過濾器,類型為org.springframework.securtiy.web.FilterChainProxy,它實作了javax.servlet.Filter,是以外部的請求會經過此類,下圖是Spring security過濾器鍊結構圖

spring secutiry oauth2.0認證制授權 --Spring secuity快速上手

FilterChainProxy是一個代理,真正起作用的是filterChainproxy中securityfilterchain所包含的各個filter,同時這些filter作為bean被spring管理,它們是spring security核心,各有各的職責,但他們并不真接處理使用者的認證,也不直接處理使用者的授權,而是它們交給了認證管理器和決策管理器進行處理,下圖是filterchainproxy相關的類的uml圖示。

spring secutiry oauth2.0認證制授權 --Spring secuity快速上手

Spring security功能的實作主要由一系列過濾鍊互相配合完成

spring secutiry oauth2.0認證制授權 --Spring secuity快速上手

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

SecurityContextPresistenceFilter 這個filter是整個攔截過程的入口和出口(也就是第一個和最後一個攔截器),會在請求開始時從配置好的securityContextRepository中擷取SecurityContext,然後把它設定給SecurityContextHolder,在請求完成後将SecurityContextHolder持有的SecrutiyContext再儲存到配置好的SecurityContextRepository,同時清除securityContextHolder所持有的SecurityContext

UserNamePasswordAuthenticatinFilter用于處理來自表單送出的認證。該表單必須提供對應的使用者名和密碼,其内部還有登入成功或失敗後處理的authticationSuccessHandler和AuthenticationFailureHandler這些都可以根據需求做相應的改變。

filterSecurityInterceptor 是用于保護web資源的,使用accessDecisionManager對目前使用者進行授權通路,前面已經詳細介紹過了。

ExceptionTranslationFilter能夠捕獲filterChain所有的異常,并進行處理,但是它隻會處理兩類異常,AuthenticationException 和AccessDeniedException ,其它的異常會繼續抛出。

認證流程

spring secutiry oauth2.0認證制授權 --Spring secuity快速上手

認證過程分析:

使用者送出使用者名、密碼被securiy filter chain中,usernamepasswordAuthenicationFilter過濾器擷取到,封裝為請求Authtication,通常情況下UsernamePasswordAuthticationtoken這個實作類

然後過濾器将Authentication送出到認證管理器(AuthenticationManger)進行認證

認證成功後,AuthenticationManger 身份管理器傳回一個被填充滿的資訊的(包括上面提到的權限資訊,身份資訊,細節資訊,但密碼通常會被移除) authentication執行個體

securtiyContextHolder安全上下文容器第3步填充的資訊authtication,通過securityContextHodler.getContext().setAuthentication方法,設定在其中

可以看出AuthenticationManger接口(認證管理器)是認證相關的核心接口,也是發起認證的出發點,它的實作類providermanger .而spring securiyt支援多種實作方式,是以provider  manger 維護着一個List

授權流程