对于网站,安全问题是不容忽视的,在用户登录时我们需要对用户密码进行校验,我们可以自己来编写逻辑,不过Spring Security框架给我提供了一套很强大安全框架,本文是一篇SpringSecurity入门级的文章,主要介绍其基本原理与认证流程以及使用。
Spring Security
1.搭建Spring Security框架
这里在搭建Spring Security框架之前你需要搭建好SpringBoot项目。然后只需添加Security框架的依赖:
<dependency>
<groupId>org.springframework.social</groupId>
<artifactId>spring-social-security</artifactId>
</dependency>
在子项目中只需添加上面依赖即可,因为在父项目中添加了Spring的管理器,它会自动管理版本问题。
如下是父项目的依赖:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.shinelon.spring</groupId>
<artifactId>shinelon_security</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<!-- 设置全局变量 -->
<properties>
<cn.shinelon.spring>1.0.0-SNAPSHOT</cn.shinelon.spring>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>Brussels-SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Dalston.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>../shinelon_app</module>
<module>../shinelon_core</module>
<module>../shinelon_demo</module>
<module>../shinelon_nexts</module>
</modules>
</project>
对于SpringBoot项目,只要你添加Spring Security框架的相关依赖,它就会自动配置到Spring容器中,开启这个功能,所以,在添加好上面的依赖后,启动项目就可以看到效果。
如上图所示,当从浏览器发送请求的时候,就会弹出一个弹窗,要求你输入用户名以及密码,默认的用户名是user,密码可以在控制台日志中找到,一个加密过的字符串。输入后,我们就会看到请求的响应。
我们还可以在你发送请求时不出现弹窗,而是通过一个登录页面来认证。
需要添加一个类继承WebSecurityConfigurerAdapter类:
package cn.shinelon.security.browser;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
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.AuthenticationException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import cn.shinelon.security.core.SecurityProperties;
import cn.shinelon.security.core.validate.code.ValidateCodeFilter;
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
//注入密码加密的类
@Bean
public PasswordEncoder passwordEncoder() {
return new SCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
// http.httpBasic() 和之前一样出现弹窗
.and()
.authorizeRequests()
.anyRequest() //任何请求
.authenticated(); //都需要认证
}
}
编写上面的代码之后,当你发送请求后就会被重定向到一个login页面来验证。
介绍完Spring Security的基本使用后,下面来讲讲如何真正的在一个应用中添加Spring Security来进行验证。
默认的用户的信息被封装在UserDetailsService这个接口中,我们可以实现这个接口,然后将数据库中密码取出来进行校验,下面是自己编写的一个实现了这个接口的类,只是简单模拟一下如何使用,没有连接数据库。
package cn.shinelon.security.browser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class MyUserDetailServices implements UserDetailsService{
@Autowired
private PasswordEncoder passwordEncoder;
//这里可以注入DAO层从数据库中取出密码进行校验
private Logger log=LoggerFactory.getLogger(getClass());
/**
* 从数据库中获取密码与前端提交的密码进行对比
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("用户名:"+username);
//指定用户授权登录信息
//密码加密,使用的是盐值加密
String password=passwordEncoder.encode("123456");
log.info("数据库中密码是:"+password);
//User类是Spring内置的一个类,实现了UserDetails接口,而这个接口是UserDetailSerice的子接口
return new User(username, password,
true,true,true,true, //用户被锁定
AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
在这个实现类中重载了一个方法loadUserByUsername,这个方法的返回时UserDetail接口,在这个接口中分装这一系列的认证参数,比如账户是否过期,是否被冻结等等信息,可以通过设置参数来满足我们实际业务中的认证需求。
UserDetails源码如下:
public interface UserDetails extends Serializable {
// ~ Methods
//
String getPassword();
String getUsername();
boolean isAccountNonExpired();
boolean isCredentialsNonExpired();
boolean isEnabled();
}
在介绍完Spring Security的基本使用以及如何在项目中使用后,下面来看看它的基本原理。
3.Spring Security的基本原理
下面是Spring Security的简单流程图:
实际上,Spring Security框架在你发送来请求后会经过一个过滤器链然后才会到达我们自己编写的API,首先,当接收到请求后,它会到达第一个过滤器UsernamePassowordAuthenticationFilter,然后就是BasicAuthenticationFilter,中间还有很多过滤器,最后两个是ExceptionTranslationFilter和FilterSecurityInterceptor。我们也可以从源码中看到:
进入到formLogin方法中
这里,它会在父类构造器中创建第一个过滤器,然后获取username和password参数。
在这个父类中,它实际上会重定向到/login的登录页面。
在简单看了一下源码之后,下面来完整的描述一下它的整个工作流程:
当浏览器发送一个请求后,首先会经过第一个过滤器,UsernamePassowordAuthenticationFilter,这个过滤器中,它会判断你的请求是否带username和password这两个参数,如果带了就进行拦截验证,如果没有就进入到第二个过滤器BasicAuthenticationFilter,这个过滤器会判断请求头中是否含有需要验证的信息,如果没有进入下一个拦截器,最后到ExceptionTranslationFilter拦截器,它会捕获FilterSecurityInterceptor抛出的异常,而FilterSecurityInterceptor会判断这个请求是否校验通过,权限是否通过,如上面我们编写的代码,所有的请求都需要进行校验通过,如果没有通过就会抛出异常,在这个异常处理中,实际上重定向到第一个过滤器,比如我们发送http://localhost:8080/user这个请求,它就经过前面的过滤器,然后在ExceptionTranslationFilter过滤器中抛出异常,再被重定向到第一个过滤器,也就是登录页面,当输入的用户名密码匹配的时候才会校验通过,进入我们自己编写的API中。以上就是Spring Security框架的一个基本认证流程,FilterSecurityInterceptor是这个框架的最后一个拦截器,也是守门人,所有的请求都必须通过该拦截器的校验。