在介绍Spring Securiry之前,我们试想一下如果我们自己去实现一个安全框架,我们需要包含哪些功能:
我们需要对登录接口或者一些不需要权限的接口放行,同时我们需要对某些接口进行身份认证,例如:在基于jwt的认证体系中,我们需要校验token是否合法,token合法我们才会放行;我们希望我们写的安全框架能够做一些授权的操作,比如:我们可以限制认证后的用户访问/user接口需要什么权限,访问/group接口又需要什么权限;我们希望安全框架能够提供一个缓存,无论是TreadLocal、还是HttpServletRequest也罢,只要能够获取保存当前认证通过的用户信息即可;试想一下,如果我们去实现这些功能,我们需要怎么做:
我们需要去拦截所有的HTTP请求,我们首先想到的实现方式就是filter、Spring AOP、Intercepter,这三者的实现方式和应用场景都不一样,这里我们不去细究采用哪种方式,但是我可以告诉你Spring Security是采用了一系列的filter实现的。假设我们也是采用的filter实现,那么我们是不是也要实现一个白名单啊,比如放行/login接口啊,然后剩下的接口,就要走认证流程;认证完之后,我们怎么做授权呢,我们可以这么做,我们先获取当前登录用户所拥有的权限,比如某某用户对接口资源user具有添加权限,采用这种格式:interface:user:add,我们将若干个这种格式的权限放到一个list,然后放到缓存中["interface:user:add","interface:group:delete",...]然后我们干一件什么事呢,我们搞个@PreAuthorize注解,然后再搞个注解处理器,注解处理器可以使用Spring AOP去实现。这个注解怎么用呢,我们可以将这个注解加在UserController /user/add接口上:@PostMapping("/user/add")@PreAuthorize("interface:user:add")public NgspResponseEntity insertUser(...)如果用户去访问/user/add接口,我们就去缓存中拉取用户的权限列表,然后去校验用户是否具有访问这个接口的权限,如果有那么我们就放行。当然了上面只是一个简单的实现,Spring Security的实现那是太太太复杂了,他为了满足各种需求,允许我们自己去配置各种过滤器,功能是强大了,但是学习起来还是比较困难的。
下面我将带领大家来学习Spring Security框架,Spring Security是一款基于Spring的安全框架,主要包含认证和授权两大安全模块,和另外一款流行的安全框架Apache Shiro相比,它拥有更为强大的功能。Spring Security也可以轻松的自定义扩展以满足各种需求,并且对常见的Web安全攻击提供了防护支持。如果你的Web框架选择的是Spring,那么在安全方面Spring Security会是一个不错的选择。
一、开启Spring Security1、导入依赖创建一个Spring Boot项目springboot-springsecurity,然后引入spring-boot-starter-security:
org.springframework.bootspring-boot-starter-security2、创建Controller新建包com.zy.example.controller,接下来我们创建一个TestController,对外提供一个/hello服务:
package com.zy.example.controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RestController;/** * @Author: zy * @Description: 测试 * @Date: 2020-2-9 */@RestControllerpublic class TestController {@GetMapping("hello")public String hello() {return "hello spring security";}}3、新建App入口新建入口程序App.java:
package com.zy.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * @Author: zy * @Description: 启动程序 * @Date: 2020-2-9 */@SpringBootApplicationpublic class App {public static void main(String[] args){SpringApplication.run(App.class,args);}}这时候我们直接启动项目,访问http://localhost:8080/hello,可看到页面弹出了个formLogin认证框:
这个配置开启了一个form Login类型的认证,所有服务的访问都必须先过这个认证,默认的用户名为user,密码由Sping Security自动生成,回到IDE的控制台,可以找到密码信息:
Using generated security password: a77c9456-901e-4848-a221-3822347e52ea输入用户名user,密码a77c9456-901e-4848-a221-3822347e52ea后,我们便可以成功访问/hello接口。
二、基于HTTP basic类型的认证我们可以通过一些配置将表单认证修改为基于HTTP Basic的认证方式。
1、配置Spring Security创建包com.zy.example.config,创建一个配置类WebSecurityConfig继承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter这个抽象类并重写configure(HttpSecurity http)方法。WebSecurityConfigurerAdapter是由Spring Security提供的Web应用安全配置的适配器:
package com.zy.example.config;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;/** * @Author: zy * @Description: spring security配置类 * @Date: 2020-2-9 */@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {/** * 配置拦截请求资源 * @param http:HTTP请求安全处理 * @throws Exception */@Overrideprotected void configure(HttpSecurity http) throws Exception {http.httpBasic() //HTTP Basic认证方式.and().authorizeRequests() // 授权配置.anyRequest() // 所有请求.authenticated(); // 都需要认证}}Spring Security提供了这种链式的方法调用。上面配置指定了认证方式为HTTP Basic登录,并且所有请求都需要进行认证。
这里有一点需要注意,我没并没有在Spring Security配置类上使用@EnableWebSecurity注解。这是因为在非Spring Boot的Spring Web MVC应用中,注解@EnableWebSecurity需要开发人员自己引入以启用Web安全。而在基于Spring Boot的Spring Web MVC应用中,开发人员没有必要再次引用该注解,Spring Boot的自动配置机制WebSecurityEnablerConfiguration已经引入了该注解,如下所示:
package org.springframework.boot.autoconfigure.security.servlet;// 省略 imports 行@Configuration// 仅在存在 WebSecurityConfigurerAdapter bean 时该注解才有可能生效// (最终生效与否要结合其他条件综合考虑)@ConditionalOnBean(WebSecurityConfigurerAdapter.class)// 仅在不存在 springSecurityFilterChain 时该注解才有可能生效// (最终生效与否要结合其他条件综合考虑)@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)// 仅在 Servlet 环境下该注解才有可能生效// (最终生效与否要结合其他条件综合考虑)@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)@EnableWebSecurity //