SpringBoot-SpringSecurity基本使用-源码剖析

简介

在web应用开发中,安全无疑是十分重要的。在之前使用filter来进行过滤,实现安全,但是要写大量的代码,而且实现的效果还很一般。spring提供Spring Security来保护web应用,是一个非常好的选择。Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成。特别是在spring boot项目中加入spring security更是十分简单。

案例介绍

在这里插入图片描述

案例细节

引入依赖

<dependencies>
        <!--thymeleaf模板引擎启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <!--thymeleaf-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-java8time</artifactId>
        </dependency>
        
        <!--security-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
    </dependencies>

核心控制器

import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class RouterController {
	//设置访问主页
    @RequestMapping({"/","/index"})
    public String index(){
        return "index";
    }
	//设置访问登陆页面
    @RequestMapping("/toLogin")
    public String toLogin(){
        return "views/login";
    }
	//设置访问level1下的页面
    @RequestMapping("/level1/{id}")
    public String level1(@PathVariable("id")int id){
        return "views/level1/"+id;
    }
	//设置访问level2下的页面
    @RequestMapping("/level2/{id}")
    public String level2(@PathVariable("id")int id){
        return "views/level2/"+id;
    }
	//设置访问level3下的页面
    @RequestMapping("/level3/{id}")
    public String level3(@PathVariable("id")int id){
        return "views/level3/"+id;
    }
}

自定义SpringSecurity安全配置

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception 
        http.authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/level1/**").hasRole("vip1")
                .antMatchers("/level2/**").hasRole("vip2")
                .antMatchers("/level3/**").hasRole("vip3")
    http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");

        http.csrf().disable();
        //http.logout().deleteCookies("remove").invalidateHttpSession(true);
        http.logout().logoutSuccessUrl("/");

        http.rememberMe().rememberMeParameter("remeber");
    }
    
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("惠杰超可爱").password(new BCryptPasswordEncoder().encode("123")).roles("vip2","vip3")
                .and()
                .withUser("阿猛").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");
    }
}

源码分析

@EnableWebSecurity

自动开启Springsecurity功能,@EnableWebSecurity注解有两个作用,1: 加载了WebSecurityConfiguration配置类, 配置安全认证策略。2: 加载了AuthenticationConfiguration, 配置了认证信息。
来看看他是怎样实现的。

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import({ WebSecurityConfiguration.class,
		SpringWebMvcImportSelector.class,
		OAuth2ImportSelector.class })
@EnableGlobalAuthentication
@Configuration
public @interface EnableWebSecurity {

	/**
	 * Controls debugging support for Spring Security. Default is false.
	 * @return if true, enables debug support with Spring Security
	 */
	boolean debug() default false;
}

@EnableGlobalAuthentication

@EnableWebSecurity引入了@EnableGlobalAuthentication注解,

@Retention(value = java.lang.annotation.RetentionPolicy.RUNTIME)
@Target(value = { java.lang.annotation.ElementType.TYPE })
@Documented
@Import(AuthenticationConfiguration.class)
@Configuration
public @interface EnableGlobalAuthentication {
}

在这个注解类中,又@Import(AuthenticationConfiguration.class)引入了AuthenticationConfiguration配置类, 这个类是来配置认证相关的核心类, 这个类的主要作用是,向spring容器中注入AuthenticationManagerBuilder,其实是使用了建造者模式, 他能建造AuthenticationManager,这是身份认证的入口。

import - WebSecurityConfiguration.class

@Import({ WebSecurityConfiguration.class,SpringWebMvcImportSelector.class,OAuth2ImportSelector.class }):激活引入了WebSecurityConfiguration配置类。进入这个类我们可以看见有很多@Bean和@Autowrite,以为和这个这个配置类,准备了很多bean对象在容器中。下面是一个很重要的SpringSecurityFilterChain这是Spring Secuity的核心过滤器, 这是请求的认证入口,字面意思就可以知道他是spring的安全过滤器

//AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME定义在这个类中
public abstract class AbstractSecurityWebApplicationInitializer
		implements WebApplicationInitializer {
	private static final String SERVLET_CONTEXT_PREFIX = "org.springframework.web.servlet.FrameworkServlet.CONTEXT.";
	public static final String DEFAULT_FILTER_NAME = "springSecurityFilterChain";
	private final Class<?>[] configurationClasses;
}

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {
	/**
	 * Creates the Spring Security Filter Chain
	 * @return the {@link Filter} that represents the security filter chain
	 * @throws Exception
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	//AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME定义在上面类中
	public Filter springSecurityFilterChain() throws Exception {
		boolean hasConfigurers = webSecurityConfigurers != null
				&& !webSecurityConfigurers.isEmpty();
		if (!hasConfigurers) {
			WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor
					.postProcess(new WebSecurityConfigurerAdapter() {
					});
			webSecurity.apply(adapter);
		}
		return webSecurity.build();
	}
}

WebSecurityConfigurerAdapter

@Order(100)
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer<WebSecurity> {
}

他实现了WebSecurityConfigurer接口,WebSecurityConfigurer接口,
这个类是 网络安全配置器适配器,适配器类的作用就是,他实现接口的方法,我们要使用接口时,不需要去直接实现接口(那就必须得实现里面的所有方法),而是通过继承适配器类(用哪个方法,重写那个方法即可)的方式。
例如:我们在配置的时候,需要我们自己写个配置类去继承他,然后编写自己所特殊需要的配置即可。
在这里插入图片描述
这就是我们可以重写的方法。

自定义配置类再分析

springsecurity采用链式编程风格,所以你看见的配置全是链式的。
我们通过分析WebSecurityConfigurerAdapter 类中我们用到的这两个方法的源码来分析。

授权分析

/**
	 * Override this method to configure the {@link HttpSecurity}. Typically subclasses
	 * should not invoke this method by calling super as it may override their
	 * configuration. The default configuration is:
	 * 
	 * <pre>
	 * http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
	 * </pre>
	 *
	 * @param http the {@link HttpSecurity} to modify
	 * @throws Exception if an error occurs
	 */
	// @formatter:off
	protected void configure(HttpSecurity http) throws Exception {
		logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

		http
			.authorizeRequests()
				.anyRequest().authenticated()
				.and()
			.formLogin().and()
			.httpBasic();
	}
}

那段注释中文翻译为:重写此方法以配置{@linkHttpSecurity}。 典型的子类不应调用super来调用此方法,因为它可能覆盖它们的配置。 默认配置是:http.authorizeRequests().anyRequest().authenticated().and().formLogin().and().httpBasic();
在这里插入图片描述

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //授权
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //请求授权的规则
        http.authorizeRequests()//拦截url注册
                .antMatchers("/").permitAll()//匹配“/”,即主页所有人可以访问
                .antMatchers("/level1/**").hasRole("vip1")//配置“/level1”开头的所有请求,只有vip1权限的人才可以访问
                .antMatchers("/level2/**").hasRole("vip2")//配置“/level2”开头的所有请求,只有vip2权限的人才可以访问
                .antMatchers("/level3/**").hasRole("vip3");//配置“/level3”开头的所有请求,只有vip3权限的人才可以访问

        //没有权限时跳转登陆页面
        //http.formLogin();//默认登录页面,跳转到springsecurity自己定义的login页面去,1.0的版本比较简陋,到2.0时这个默认登陆界面已经很漂亮了
        http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login");//跳转到自定义登陆页
        /*
		这里要注意,springsecurity自定义的login页面中,用户名和密码的name属性值是username和password,这里的usernameParameter(String str)、passwordParameter(String str)方法就是当我们自定义的登陆页面中这两个输入框的name属性值不一致设计的。像我们用户名和密码的name属性值分别是user和pwd。
		*/

        //注销,并跳到主页
        http.csrf().disable();//关闭csrf功能,这是登陆失败可能的原因
        //http.logout().deleteCookies("remove").invalidateHttpSession(true);//也可以实现
        http.logout().logoutSuccessUrl("/");//点击注销功能,请求“/”,即主页面

        //开启记住我功能
        http.rememberMe().
        	rememberMeParameter("remeber"); //用于指示在登录时记住用户的HTTP参数。
    }
}

在这里插入图片描述

认证分析

认证方法。

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
   //认证
   //密码编码:PasswordEncoder类
   //在springsecurity5.0+新增了很多加密方法
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {

       //这里是从内存中读取的固定用户,一般是从数据库读取的,但是密码需要加密
       //对密码加密,BCryptPasswordEncoder只是密码加密的一种
       auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
               .withUser("惠杰超可爱").password(new BCryptPasswordEncoder().encode("123")).roles("vip2","vip3")
               .and()
               .withUser("阿猛").password(new BCryptPasswordEncoder().encode("123")).roles("vip1");
   }
}

在这里插入图片描述
在这里插入图片描述

相关类再分析

授权核心类HttpSecurity

类似于spring security的xml配置文件命名空间配置中的元素。它允许对特定的http请求基于安全考虑进行配置。
默认情况下,适用于所有的请求,但可以使用requestMatcher(RequestMatcher)或者其它相似的方法进行限制。

认证核心类AuthenticationManagerBuilder

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值