Spring Security
Spring Security
Spring Security 是 Spring 家族中的一个安全管理框架,但是对比 Shiro 使用的并不多。在Spring Boot中,对 Spring Security 提供了自动化配置方案,可以零配置使用 Spring Security。
常见的安全管理技术栈的组合:
- SSM(Spring+SpringMVC+MyBatis) + Shiro
- Spring Boot/Spring Cloud + Spring Security
核心功能:
- 认证(你是谁)
- 授权(你能干什么)
- 攻击防护(防止伪造身份)
项目创建
首先创建spring boot项目,添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
创建一个 HelloController:
@RestController
public class HelloSecurityController {
@GetMapping("/hello")
public String hello() {
return "hello";
}
}
访问 /hello ,需要登录之后才能访问:
当用户从浏览器请求访问 /hello 时,会重定向到 /login 页面,用户登陆成功之后就会自动跳转到 /hello 。
默认情况下,登录的用户名是 user ,密码从启动的控制台日志中得到。
也可以使用 POSTMAN 来发送请求,将用户信息放在请求头中(可以避免重定向到登录页面)。
由此知Spring Security 支持两种不同的认证方式:
- 可以通过 form 表单来认证
- 可以通过 HttpBasic 来认证
用户名配置
对登录的用户名/密码进行配置,有三种不同的方式:
- 在 application.properties 中进行配置
- 通过 Java 代码配置在内存中
- 通过 Java 从数据库中加载
在 application.properties 文件中配置用户的基本信息:
spring.security.user.name=test
spring.security.user.password=123456
基于内存来存储用户信息
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER").and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("456")).roles("USER","ADMIN");
}
}
过滤器的使用
对于登录成功或失败后的响应,都可以在 WebSecurityConfigurerAdapter 的实现类中进行配置。
WebSecurityConfigurerAdapter类:通过重载该类的三个configure()方法来制定Web安全的细节。
- configure(WebSecurity):通过重载该方法,可配置Spring Security的Filter链
- configure(HttpSecurity):通过重载该方法,可配置如何通过拦截器保护请求
- configure(AuthenticationManagerBuilder):通过重载该方法,可配置user-detail(用户详细信息)服务
创建Spring Security的配置类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests() //开启登录配置
.antMatchers("/", "/home").permitAll() //访问"/"和"/home"路径的请求都允许
.anyRequest().authenticated() //表示剩余的其他接口,登录之后访问
.and()
.formLogin() //表单登陆
//定义登录页面,未登录时,访问一个需要登录之后才能访问的接口,会自动跳转到该页面
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
//基于内存来存储用户信息
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("123")).roles("USER").and()
.withUser("admin").password(new BCryptPasswordEncoder().encode("456")).roles("USER","ADMIN");
}
}
Controller层代码
@Controller
public class SecurityController {
@GetMapping(value = {"/home","/"})
public String home(){
return "home";
}
@GetMapping(value = "/hello")
public String hello(){
return "hello";
}
@GetMapping(value = "/login")
public String login() {
return "login";
}
}
模板页面放在resources/templates目录下
home.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Spring Security Example</title>
</head>
<body>
<h1>Welcome!</h1>
<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
</body>
</html>
hello.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Hello World!</title>
</head>
<body>
<h1 th:inline="text">Hello [[${#httpServletRequest.remoteUser}]]!</h1>
<form th:action="@{/logout}" method="post">
<input type="submit" value="Sign Out"/>
</form>
</body>
</html>
login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>登陆</title>
</head>
<body>
<h3>表单登陆</h3>
<div th:if="${param.error}">
Invalid username and password.
</div>
<div th:if="${param.logout}">
You have been logged out.
</div>
<form th:action="@{/login}" method="post">
<tr>
<td>用户名:</td>
<td><input type="text" name="username"></td>
</tr>
<tr>
<td>密码:</td>
<td><input type="password" name="password"></td>
</tr>
<tr>
<td colspan="2"><button type="submit">Sign In</button></td>
</tr>
</form>
</body>
</html>
在pom.xml文件中单独添加Thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
运行结果
localhost:8080/home可直接访问,而直接访问 localhost:8080/hello 时,页面将跳转到 localhost:8080/login。在login界面输入SecurityConfig中配置的用户名及密码,将成功跳转到hello界面。
忽略拦截
如果某一个请求地址不需要拦截的话,有两种方式实现:
- 设置该地址匿名访问
- 直接过滤掉该地址,即该地址不走 Spring Security 过滤器链
第二种方案:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/vercode");
}
}