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
授权流程