天天看点

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

授权流程