Spring Security修炼手册(一)————初识Security

    在以前做东西的时候,对于认证鉴权的框架技术选型,通常使用Apache Shiro,可能是接触比较早,感觉用起来比较方便的原因,知道最近接了一个好大好大的项目分布式应用+大数据数据库+私有IaaS云+PaaS,埋头苦学一星期,算是吃透了Spring Security的一半,那么一是为了记录在学习过程中的知识点,二是为了让没有接触过安全框架,或者还在使用数据库,自己写Filter比较原始方式做权限控制的朋友提供一个学习的参考。那么废话不说太多,下面开始一步一步来。

  一、初识Spring Security

        首先看名字就可以看出来,这个框架出自于Pivotal 的Spring系列。Spring Security 提供了基于javaEE的企业应用,全面的安全服务。 大家使用Spring Secruity的原因有很多,但是大部分都是发现了由于javaEE的Servlet规范或EJB规范中的安全功能缺乏典型企业应用场景所需的深度。 那么Security提供的 “认证”和“授权”(或者访问控制) 是整个框架也是本系列博客所要讲解的重要内容。

二、引入Security依赖

    Security官方链接:https://projects.spring.io/spring-security/

    打开你的IED,在Maven坐标如下:想使用其他版本请去如上链接,如果你之前接触过Spring IO那么无需指定Version.

<dependencies>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>5.0.5.RELEASE</version>
    </dependency>
</dependencies>

  Gradle如下:

dependencies {
    compile 'org.springframework.security:spring-security-web:5.0.5.RELEASE'
}

三、Security的基本认证功能的使用

    首先明确一个事情,Spring Security所有提供给开发者的认证和鉴权功能都是基于过滤器链的,而我看到的大部分讲解Security的博客和资料中,却很少提及每个功能作用的过滤器是谁?过滤器的执行流程是怎么样的!我之前在学习Docker和K8S的时候买的一本书讲解的非常透彻,作者不仅仅在讲怎么用!而是从:是什么?怎么用?为什么?这三方面来说,因此我会在文中通过Debug或流程图,为大家深入的讲解这款框架的东西。

    3.1、HttpBasic认证

        那么当你引入了Security的依赖后,无需做其他任何的事情,启动项目,访问的时候你就会发现你的应用已经有了一个基本的认证功能,其实前面说的话不是完全正确的,由于笔者之前使用的Spring Boot版本为1.5,依赖的Security 4.X版本,所以无需任何配置,启动项目访问则会弹出默认的httpbasic认证,那么本次Demo是基于security5.X ,Spring Boot2.X版本去做的,经过我翻阅官方文档和Spring Boot2.X的properties,在这个5.X的版本中已经将默认的认证方式更改为表单认证,并且security.basic.enabled属性同样是过期了的。

                                        

    下面的图是官方给出的属性配置,已经无security.basic.enabled

    

        在这里笔者十分负责任的说,如果想开启httpbasic认证,需要在配置类中进行配置才可以,亲测通过properties配置不生效,开启httpbasic认证的代码如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http
		   .httpBasic()//开启httpbasic认证
		.and()
		   .authorizeRequests()//Security表达式
	             .anyRequest().authenticated();//所有请求都需要认证
	}

}

    启动项目,在启动的时候会发现控制台有如下输出,这个字符串就是Security默认生成的认证密码。

    访问应用,弹出httpbasic,通过生成的默认密码进行认证,不过这种方式基本不太会使用,不着重介绍。

    3.2、表单认证

        表单认证是大部分业务场景中都需要使用的一种认证方式,那么如何配置,又有哪些可配置的呢?看如下代码:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurer
Adapter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http
		.formLogin()
			.loginPage("/loginPage.html")//用户未认证时,转跳到认证的页面
			.loginProcessingUrl("/toLogin")//form中action的地址,也就是处理认证请求的URL	
			.usernameParameter("UM")//form中用户名密码的name名
			.passwordParameter("PW")
			.defaultSuccessUrl("/index")//认证成功后默认转跳的URL
		.and()
		.authorizeRequests()
			.antMatchers("/loginPage.html","/toLogin").permitAll()//最后不要忘记将自定义的
                                                                           //如上不需要认证的URL加进来
			.anyRequest().authenticated();
	}

}

我们访问任意需要认证的URL,发现会自动转跳到配置的认证页面。

 我们暂时先使用内存用户,在properties中配置一个admin用户,模拟登录。

 登陆后,自动转跳到index的controller中,对了!如果你使用了自己的登录页面,记得将CSRF防护功能关掉哦,否则你讲无法进行认证。

四、基于Spring的配置

    那么通过以上代码的讲解,你应该对于认证功能的基本使用有了一定的了解,但是上面的代码中有一个开发界非常难以容忍的问题,就是含有大量的硬编码!!!那么我们如何解决的?

首先我们创建三个类,分别叫:SecurityProperties、ToLoginProperties、LoginProperties,代码分别如下:

public class LoginProperties {
	
	private String loginPage = "/loginPage.html";
	
	private String loginProcessingUrl = "/toLogin";
	
	private String usernameParameter = "UM";
	
	private String passwordParameter = "PW";
	
	private String defaultSuccessUrl = "/index";

	public String getLoginPage() {
		return loginPage;
	}

	public void setLoginPage(String loginPage) {
		this.loginPage = loginPage;
	}

	public String getLoginProcessingUrl() {
		return loginProcessingUrl;
	}

	public void setLoginProcessingUrl(String loginProcessingUrl) {
		this.loginProcessingUrl = loginProcessingUrl;
	}

	public String getUsernameParameter() {
		return usernameParameter;
	}

	public void setUsernameParameter(String usernameParameter) {
		this.usernameParameter = usernameParameter;
	}

	public String getPasswordParameter() {
		return passwordParameter;
	}

	public void setPasswordParameter(String passwordParameter) {
		this.passwordParameter = passwordParameter;
	}

	public String getDefaultSuccessUrl() {
		return defaultSuccessUrl;
	}

	public void setDefaultSuccessUrl(String defaultSuccessUrl) {
		this.defaultSuccessUrl = defaultSuccessUrl;
	}
	
	
}
public class ToLoginProperties {
	
	private LoginProperties loginProperties = new LoginProperties();

	public LoginProperties getLoginProperties() {
		return loginProperties;
	}

	public void setLoginProperties(LoginProperties loginProperties) {
		this.loginProperties = loginProperties;
	}

}
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "jy.security")
public class SecurityProperties {

	private ToLoginProperties toLoginProperties = new ToLoginProperties();

	public ToLoginProperties getToLoginProperties() {
		return toLoginProperties;
	}

	public void setToLoginProperties(ToLoginProperties toLoginProperties) {
		this.toLoginProperties = toLoginProperties;
	}
	
}

    通过以上代码,我们就可以读取到配置文件中,以“jy.security”开头的属性,并将属性对应的值注入到其中,这样当我们配置了对应属性的时候,值即被替换,未配置的时候则走我们的默认值。

然后我们把securityconfig类在修改一下,不在使用硬编码,而是将SecurityProperties类注入。

import org.springframework.beans.factory.annotation.Autowired;
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 com.spring.demo.peoperties.SecurityProperties;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	
	@Autowired
	private SecurityProperties securityProperties;
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		
		http
			.formLogin()
				.loginPage(securityProperties.getToLoginProperties().getLoginProperties().getLoginPage())//用户未认证时,转跳到认证的页面
				.loginProcessingUrl(securityProperties.getToLoginProperties().getLoginProperties().getLoginProcessingUrl())//form中action的地址,也就是处理认证请求的URL	
				.usernameParameter(securityProperties.getToLoginProperties().getLoginProperties().getUsernameParameter())//form中用户名密码的name名
				.passwordParameter(securityProperties.getToLoginProperties().getLoginProperties().getPasswordParameter())
				.defaultSuccessUrl(securityProperties.getToLoginProperties().getLoginProperties().getDefaultSuccessUrl())//认证成功后默认转跳的URL
		.and()
		.authorizeRequests()
			.antMatchers(securityProperties.getToLoginProperties().getLoginProperties().getLoginPage(),
					     securityProperties.getToLoginProperties().getLoginProperties().getLoginProcessingUrl()).permitAll()
			.anyRequest().authenticated()
		.and()
			.csrf().disable()
			;
	}
	
	

}

    最后我们做一个测试,证明通过配置文件的方式,可以对我们的配置类修改,我们修改未认证时转跳的认证页面的URL为“/loginTest”

重启项目,访问/index,结果如下:404是由于我根本没写这个页面,但已经可以证明我们的配置生效,避免了硬编码的问题

 在下一篇博客里,我会介绍自定义身份认证和一些处理器的用法及源码,敬请关注!

猜你喜欢

转载自my.oschina.net/u/3637243/blog/1820408