SpringSecurity核心功能
- 认证(你是谁)
- 授权(你能干什么)
- 攻击防护(防止伪造身份)
内容简介
- SpringSecurity基本原理
- 实现用户名+密码认证
- 实现手机号+短信认证
实例
当我们在一个spring boot项目的pom.xml文件中加入SpringSecurity依赖:
<dependency>
<groupId>com.yun.security</groupId>
<artifactId>spring-security-browser</artifactId>
<version>${spring.security.version}</version>
</dependency>
接着我们编写一个简单的GET方式的restful请求。
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("{id:\\d+}")
public User query() throws Exception{
User user= new User();
user.setUsername("yun");
user.setBirthday(new Date());
user.setPassword("123456");
return user;
}
}
当我们在浏览器上访问:http://localhost:8080/user/1
后,页面没有直接显示{"username":"yun","password":"123456","birthday":1511071575806}
这些User信息,而是先弹出一个登陆框,要求输入用户名和密码
这就是我们引入的SpringSecurity起到的默认效果,称为HTTP BASIC登陆。其中用户名默认为user,密码需要在启动日志里找到:
Using default security password: 428f0613-d610-4809-89fb-dd14e9b7c8a7
将用户名和密码输入后,页面才显示restful api返回的具体信息:
{"username":"yun","password":"123456","birthday":1511071575806}
由上可知SpringSecurity的默认行为就是给请求加上身份认证。
如果我们想覆盖上面这个默认的身份认证,改成表单登录,需要编写一个配置类进行配置。代码如下:
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception{
http.formLogin()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
其中父类WebSecurityConfigurerAdapter是一个web应用的安全方面的适配器类,configure方法的参数为HttpSecurity对象,如果想改成表单登录,调用这个对象的formLogin方法即可,后面几句是授权方面的配置。这样就实现了表单登陆进行身份认证,并且所有的请求都要进行认证。当浏览器重新访问http://localhost:8080/user/1的时候发现页面此时显示为登录表单。
如果我们还是想改回HTTP BASIC登陆页面,那个将配置类改成下面即可:
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception{
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
SpringSecurity基本原理
SpringSecurity实际是由一组过滤器链组成的,当客户端发起一个请求后,需要经过这组过滤器链才能到达restful api;同样地,响应也是通过这组过滤器链才能返回到客户端。
由上图可知当发起一个请求后,会经过Username Password Authentication Filter过滤器,这个过滤器用于判断请求中是否包含用户名和密码的相关信息,如果有则进行登录认证,如果没有则跳过这个过滤器到Bisic Authentication Filter过滤器,这样一步一步经过后面的过滤器,FilterSecurityInterceptor是最后一个过滤器,经过后就到restful api了。FilterSecurityInterceptor过滤器如果抛出异常后,就会被前面这个Exception Translation Filter处理异常。
注意
上述过滤器链中绿色的过滤器可以通过配置选择,像上面的配置类中我们执行http.httpBasic()
时则执行Basic Authentication Filter,如果是执行http.formLogin()
则执行Username Password Authentication Filter。而蓝色和橘色的过滤器则是不能更改顺序和去掉的。
测试
为了验证这个流程,我们在上面这几个过滤器类中打断点,首先在Username Password Authentication Filter类中打断点如下:
接着在Exception Translation Filter类中打断点:
然后在FilterSecurityInterceptor类中打断点:
最后在restful api中打断点,打完后,启动服务,浏览器访问地址:http://localhost:8080/user/1后,发现程序直接执行到Exception Translation Filter的第一个断点,这是因为请求中没有用户名和密码相关信息,点击单步调试后跳到FilterSecurityInterceptor类中的断点处,在全速运行后程序又跳到Exception Translation Filter的第二个断点处,此时已是到了异常的catch部分,再全速运行后,程序直接结束,页面重定向到表单的用户名密码登陆页面,填完用户名密码登陆后,此时程序在Username Password Authentication Filter类中的断点停住了,说明此时请求中携带着用户名和密码信息,程序执行到了Username Password Authentication Filter这个类的方法中。全速运行后,程序先后执行了异常处理过滤器和FilterSecurityInterceptor过滤器和最后的restful api中。从上述过程中,发现最后这个FilterSecurityInterceptor过滤器其实是包裹在异常处理过滤器中的,所以当第一次请求没有携带用户登陆信息的时候会抛出异常被异常处理过滤器捕捉并进行页面的重定向。同时也验证了整个过程是正确的。