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過濾器鍊結構圖

FilterChainProxy是一個代理,真正起作用的是filterChainproxy中securityfilterchain所包含的各個filter,同時這些filter作為bean被spring管理,它們是spring security核心,各有各的職責,但他們并不真接處理使用者的認證,也不直接處理使用者的授權,而是它們交給了認證管理器和決策管理器進行處理,下圖是filterchainproxy相關的類的uml圖示。
Spring security功能的實作主要由一系列過濾鍊互相配合完成
下面介紹過濾器鍊中主要的幾個過濾器及其作用
SecurityContextPresistenceFilter 這個filter是整個攔截過程的入口和出口(也就是第一個和最後一個攔截器),會在請求開始時從配置好的securityContextRepository中擷取SecurityContext,然後把它設定給SecurityContextHolder,在請求完成後将SecurityContextHolder持有的SecrutiyContext再儲存到配置好的SecurityContextRepository,同時清除securityContextHolder所持有的SecurityContext
UserNamePasswordAuthenticatinFilter用于處理來自表單送出的認證。該表單必須提供對應的使用者名和密碼,其内部還有登入成功或失敗後處理的authticationSuccessHandler和AuthenticationFailureHandler這些都可以根據需求做相應的改變。
filterSecurityInterceptor 是用于保護web資源的,使用accessDecisionManager對目前使用者進行授權通路,前面已經詳細介紹過了。
ExceptionTranslationFilter能夠捕獲filterChain所有的異常,并進行處理,但是它隻會處理兩類異常,AuthenticationException 和AccessDeniedException ,其它的異常會繼續抛出。
認證流程
認證過程分析:
使用者送出使用者名、密碼被securiy filter chain中,usernamepasswordAuthenicationFilter過濾器擷取到,封裝為請求Authtication,通常情況下UsernamePasswordAuthticationtoken這個實作類
然後過濾器将Authentication送出到認證管理器(AuthenticationManger)進行認證
認證成功後,AuthenticationManger 身份管理器傳回一個被填充滿的資訊的(包括上面提到的權限資訊,身份資訊,細節資訊,但密碼通常會被移除) authentication執行個體
securtiyContextHolder安全上下文容器第3步填充的資訊authtication,通過securityContextHodler.getContext().setAuthentication方法,設定在其中
可以看出AuthenticationManger接口(認證管理器)是認證相關的核心接口,也是發起認證的出發點,它的實作類providermanger .而spring securiyt支援多種實作方式,是以provider manger 維護着一個List
授權流程