Spring Security 是 Spring 在安全方面的推出的框架,是采用责任链的设计模式,基于 Spring AOP 和 Servlet 过滤器进行实现。这篇文章记录认证授权的一些概念,以及如何使用其进行扩展保护我们的应用。
认证与授权的概念
在系统安全方面,我们面临的两个问题是:
1、如何保护需要访问权限的资源?
在 Java 中我们一般使用过滤器对我们的资源进行保护,使用拦截器对方法的访问进行控制。
2、用户的登录状态要如何传递?
如何让服务器意识到用户已经登录过系统了?一种是使用 Session 进行认证,令一种是使用 Token (令牌) 进行认证。
Session 认证
这种认证方式的缺点是:一是不能用于移动端的认证,二是一旦客户禁用浏览器 Cookie 将会导致无法认证。以下是基于 Session 认证的流程:
1 2
| 用户密码登录 --> 服务器验证通过,为用户创建一个Session(服务器中的一小块内存区域),存储登录用户信息 --> 返回 SessionID,写入用户Cookie --> Cookie将与每个后续请求一起发送 --> 将SessionID与服务器中的信息进行比对
|
Token 认证
我们可以在用户认证成功后生成自定义令牌,保存在Redis或者内存中,返回给前端,用户后续的每次请求都在在请求头 Authorization 中携带令牌,而不是将认证信息存放在请求头 Cookie 中。之后我们将请求头中的令牌与服务器存储的令牌进行比较,验证用户的登录状态。
另外一种方案是使用 JWT。
优点:1、无需对Token
进行存储,可以直接通过计算进行校验,以时间换空间。2、可以在 Token 中存储用户的相关信息
缺点:Token 无法随时废弃。
Spring Security 配置
Spring Security 是一款高度可配置化的框架,我们可以继承或者实现相关的类,从而实现定制化功能。以下是配置的一个示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @RequiredArgsConstructor public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthSuccessHandler authSuccessHandler;
private final AuthFailureHandler authFailureHandler;
private final SimpleAccessDeniedHandler accessDeniedHandler;
private final SimpleAuthenticationEntryPoint authenticationEntryPoint;
private final JwtProperties jwtProperties;
/** * 配置不需要进行认证的接口 */ @Override public void configure(WebSecurity web) { web.ignoring() .antMatchers(HttpMethod.OPTIONS, "/**") .antMatchers("/css/**") .antMatchers("/signUp.html") .antMatchers("/user/**") .antMatchers("/common/test") .antMatchers("/test"); }
@Override public void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/login.html") .loginProcessingUrl("/login") .successHandler(authSuccessHandler) .failureHandler(authFailureHandler) .permitAll() .and() .headers() .frameOptions() .disable() .and() .authorizeRequests() .antMatchers("/login.html").permitAll() //开启认证 .anyRequest().authenticated() .and().csrf().disable() //前后端分离采用JWT 不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //添加自定义权限过滤器 .exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .accessDeniedHandler(accessDeniedHandler) .and() .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtProperties.getSecretKey())); } }
|
简单总结下,通过上面的代码,可以看出,我们可以做以下的一些配置:
1、配置不需要进行认证的路径
2、添加过滤器,可以自定义 Token 认证逻辑,主要是通过继承:BasicAuthenticationFilter,重写认证方法
3、自定义一些处理器:
- 认证成功处理器:在认证成功后需要什么后续的处理?比如:向前端返回JSON 数据,主要通过实现 AuthenticationSuccessHandler 接口
- 认证失败处理器:认证失败该做什么处理?如:返回自定义认证失败信息,在前后端分离的情况下,可以返回认证失败的 JSON 数据给前端,主要通过实现 AuthenticationFailureHandler 接口
4、访问需要权限的资源,未授权时要怎么对异常进行处理?可以抛出一些更家友好的提示等:主要实现:AuthenticationEntryPoint 接口
5、权限不足异常处理,如:前后端分离情况下,可以自定义返回 JSON 数据,主要通过实现:SimpleAccessDeniedHandler 接口
6、从哪里获取用户信息?我们可以实现:UserDetailsService 接口,实现 loadUserByUsername
方法,让程序从数据库当中获取我们的数据。
最后
所涉及的代码可以点击这里
参考链接