SpringSecurity(一)——简介及项目搭建
前言
SpringSecurity框架是一款权限管理框架,与之相同功能的还有一款Shiro框架(后续说明,本系列只说明SpringSecurity),本篇笔记会先说明权限
这个概念,然后说说明最基础的集成SpringSecurity框架的项目搭建。
文章目录
权限管理
权限管理属于系统安全的范畴,限制对系统访问权限的设置,用户必须通过某种认证访问资源或者用户认证后可以访问被授权的资源,即:权限管理包括两个部分,认证及授权。
认证可以单纯的理解为系统的登录,我们需要出示我们在系统中的合法身份来获取进入系统的权限,即:曾经的登录操作;
授权便是在认证之后的进一步操作,即便我们已经被认证为系统的合法用户,但我们也不可能对系统中的所有资源进行操作。众所周知,系统中的用户是分等级或分工的,不同等级或分工的身份所可以操作的资源是不同的,所以我们需要对用户进行授权,规定用户操作资源的范围和等级。
权限管理的框架技术
Java企业级开发中,关于权限管理的技术较少,大体分为两个技术和一个方向:
- Shiro:Java权限管理框架的老前辈,是一款轻量、易于集成的技术,但是在微服务架构中无法完全发挥自己的有点,倒是有一些鸡肋;
- SpringSecurity:该框架现在很受欢迎,因为在现阶段观察Java框架的发展趋势,Spring全家桶是一个总体趋势,且SpringSecurity在SpringBoot框架中集成使用有着更大的优势;
- 自定义规则:这方面大家了解就好,因为开发一个权限管理框架不单单是上面说的认证和授权问题,还有许多安全问题,比如数据安全、数据加密等,更主要的是框架当然要基于现有技术的基础上进行创新,但是现有技术也并不是十分安全,比如我们经常使用的
slf4j
工具(谁能想到有这么恶心的漏洞)。所以一般只有大厂才会自行开发权限框架。
SpringSecurity项目搭建
引入依赖
<!--引入SpringSecurity依赖-->
<!--引入该依赖后,便可以对所有接口进行保护,无需相应配置即可完成简单的功能-->
<!--所有接口必须经过认证后才可以继续访问-->
<!--同时,引入该依赖后,系统无论请求任何路径都会进入到登录页面,且访问不存在的资源也会进入登录页面-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
定义controller(随便写一个,方便测试)
@RestController
public class TestController{
@RequestMapping("test")
public String test(){
return "成功访问该接口";
}
}
表现形式
在SpringBoot项目中引入依赖后,我们的项目默认会将所有的接口以及页面等资源保护起来,需要认证以及授权后才可以成功访问资源(默认没有定义授权的设置,所以暂时只会说明认证)。
项目启动以后我们会在控制台中看到下图的数据:
图中的密码是SpringSecurity为我们自动生成的用户密码,默认用户名为:user
,这个时候我们访问一下test
接口会出现一个需要登录的认证页面,我们在用户名中输入user
,在密码中输入控制台中自动生成的密码,才可以访问对应资源。
修改默认用户名以及资源认证的方法
配置文件
该部分分为两个文件,一个是application.yml
,另一个则是我们自定义的配置类(用于修改认证的方式等)。
application.yml
spring:
security:
user:
name: root
password: 123123
该部分配置表示我们将SpringSecurity中默认生成的用户名及密码修改为自定义的密码,这个时候我们重启项目的时候,控制台中不会为我们生成默认的密码。
自定义配置类(配置认证规则)
我们在实际开发中,有很多资源是不需要认证就可以访问的,比如登录界面、网站主页、注册页面等,但是SpringSecurity依赖引入后会自动将项目中所有的资源进行保护起来,这个时候我们可以通过自定义配置类来修改这些。
// 配置类必须加上这个注解
@Configuration
// 自定义的认证等配置需要继承该类并重写以下方法
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表示开启请求权限管理
http.authorizeRequests().
mvcMatchers("/index") // 表示index 的所有资源
.permitAll() // 表示放行指定的请求
.mvcMatchers("/loginPage") // 表示login的资源
.permitAll()
.mvcMatchers("/mainPage")
.permitAll()
.anyRequest() // 表示剩余的资源
.authenticated()
.and() // 继续配置,该方法会返回一个HttpSecurity对象
.formLogin() // 表示需要认证的资源使用表单认证
.loginPage("/loginPage")
.loginProcessingUrl("/loginRequest") // 指定登录请求的路径,默认为login
.usernameParameter("name") // 修改用户认证的字段名,即通知认证器,前端哪个字段名用来认证
.passwordParameter("pwd")
.successForwardUrl("/mainPage") // 认证成功后页面跳转路径(转发)
// .failureForwardUrl("/loginPage") // 认证失败跳转的页面(转发)
// .failureUrl("/loginPage") // 认证失败跳转页面(重定向)
.failureHandler(new MyAuthenticationFailureHandler()) // 注销失败拦截器
// 默认认证成功路径(重定向),该方法会记录第一次请求的资源,认证成功后会优先重定向到认证前的资源路径
// 第二个参数默认为false,如果手动定义为true,则会以重定向的方式完成和 successForward方法同样的操作
// .defaultSuccessUrl("/main",true)
// 前后端分离的认证成功处理
// .successHandler(new MyAuthenticationSuccessHandler()) // 认证成功之后的处理
.and()
.logout()
// .logoutUrl("/logout") // 注销登录的URL,默认提交方式为GET
// 指定多个注销请求的路径及方式
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/logoutGet","GET"),
new AntPathRequestMatcher("/logoutPost","POST")
)
)
// 前后端分离项目中,注销处理
.logoutSuccessHandler(new MyLogoutSuccessHandler()) // 注销拦截器
.invalidateHttpSession(true) // 是否清除Session域
.clearAuthentication(true) // 是否清除认证信息
.logoutSuccessUrl("/loginPage") // 注销成功后跳转的页面
.and()
.csrf().disable() // 禁用csrf跨站请求保护
;
}
}
上面这个配置我们懒得循序渐进的一点一点加了,所有配置方法都加上了注释,且同种类型不同处理办法的配置都放在一起,大家可以自行设置。
在这个配置类中,我们配置好的登录认证页面在自定义后必须将这个页面加入到放心策略中,且所有放行的资源必须写在所有资源之前。
自定义拦截器
在上面的自定义配置类中我们有一些自定义的拦截器,用于登录认证、注销认证等,对应的自定义拦截器如下:
认证成功拦截器
// 自定义成功认证之后的处理
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> result = new HashMap<>();
result.put("result","登录成功");
result.put("msg",200);
result.put("authentication",authentication);
response.setContentType("application/json;charset=UTF-8");
String resultStr = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(resultStr);
}
}
认证失败拦截器
/**
* 自定义认证失败处理方案
*/
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String,Object> result = new HashMap<>();
result.put("msg","登录失败: "+exception.getMessage());
result.put("status",500);
response.setContentType("application/json;charset=UTF-8");
String resultStr = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(resultStr);
}
}
注销成功拦截器
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> result = new HashMap<>();
result.put("msg","注销成功,当前认证对象为:" + authentication);
result.put("status",200);
response.setContentType("application/json;charset=UTF-8");
String resultStr = new ObjectMapper().writeValueAsString(result);
response.getWriter().println(resultStr);
}
}
获取用户认证信息的方式
后端代码
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
// 获取身份信息
Object principal = authentication.getPrincipal();
// 获取权限信息
Object authorities = authentication.getAuthorities();
log.info("获取身份信息:" + principal);
log.info("获取权限信息:" + authorities);
在用户认证成功后,我们可以通过这段代码来获取认证后的用户信息。
前端代码
本次项目中使用的前端引擎为thymeleaf
,thymeleaf中并没有兼容SpringSecurity的功能,所以我们需要引入扩展依赖
<!--引入thymeleaf对SpringSecurity的依赖-->
<!--引入该依赖可以在thymeleaf中使用Security操作注册信息-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
引入该依赖以后,需要在前端页面中引入头部文件后才可以使用:
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://themeleaf.org/extras/spring-securiry">
<h1>获取用户认证信息</h1>
<li sec:authentication="principal.username"></li>
<li sec:authentication="principal.authorities"></li>
<li sec:authentication="principal.accountNonExpired"></li>
<li sec:authentication="principal.accountNonLocked"></li>
总结
以上就是SpringSecurity架构的基础搭建,需要注意的是:我们在使用页面跳转以及将一些参数手动加入到前端的对应域对象中等操作。但是在现今盛行的前后端分离的架构中,我们通常会使用自定义拦截器的方式为前端传输json格式的数据,使得前端系统可以根据传递的结果完成接下来的工作。