SpringSecurity是基于Spring的一个功能强大且可高度自定义的身份认证和授权的访问控制框架。
以下基于SpringBoot2.4.4 +SpringSecurity5.4.5为例介绍。不同版本的集成有差异。
建议用SpringSecurity5.0以上的版本。
项目搭建
根据IDEA spring向导搭建SpringBoot和SpringSecurity的web项目。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.cmdi</groupId>
<artifactId>demo3</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo3</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
此时安全认证模式自动生效。
登录模式
编码测试用LoginController。
@Controller
public class LoginController {
@RequestMapping(value = {"/","/index"})
@ResponseBody
public String index() {
String a = "index";
System.out.println(a);
return a;
}
}
HttpBasic表单提交登录
通过浏览器或postman访问http://localhost:8080/index,发现自动跳转到一个登录地址http://localhost:8080/login,要求输入用户名密码进行验证登录。
SpringSecurity默认访问控制模式采用HttpBasic表单提交方式。该方式拦截所有请求,默认的用户名是user,密码在控制台上中打印。
Using generated security password: e88b94cd-9d2e-47ac-a934-ba97eb683636
登录成功后在浏览器页面成功显示index。(为了方便做简单显示)
当然可以在springboot配置文件application.properties中配置默认的用户名密码:
spring.security.user.name=aaa
spring.security.user.password=123456
自定义formLogin登录
自定义登录方式指的是不使用SpringSecurity默认的登录页面,配置使用自写页面。
一、在resources/templates下创建mlogin.html文件为登录页面。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
</head>
<body>
<form action="/login" method="post">
<p>
<label>用户名</label>
<input type="text" id="username" name="username">
</p>
<p>
<label>密码</label>
<input type="text" id="password" name="password">
</p>
<p>
<input type="submit" value="登录">
</p>
</form>
</body>
</html>
二、为了使得前端页面可用,在配置文件中声明如下。
spring.security.user.name=aaa
spring.security.user.password=123456
spring.resources.static-locations=classpath:/templates/
三、自定义配置类,继承org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter类,重写其中的三个configure方法。
WebSecurityConfigurerAdapter用来设定认证(登录)和授权(权限)。如果不做任何配置那么默认拦截所有的请求(包括登录),例如上面的/index被拦截需要登录。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
}
}
下面进行简单的配置,复写configure(HttpSecurity http):
http.authorizeRequests():表示拦截所有的请求,并进行处理。
antMatchers("/login","/mlogin.html").permitAll():对于登录页面和登录请求不拦截(满足条件的permitAll一律放行)。可以添加多个资源地址。其中/login为SpringSecurity默认的登录请求提交地址。
anyRequest().authenticated():其他的任何请求,全部拦截。
and():表示再返回一个HttpSecurity http方便链式设置。
formLogin().loginPage("/mlogin.html"):表示配置表单登录,并设置登录页面为/mlogin.html。
loginProcessingUrl("/login"):登录页面点提交后的请求地址(/login为SpringSecurity默认的登录请求提交地址)。
usernameParameter("username")、passwordParameter("password"):登录页面form表单中用户名、密码输入框对应的name值。(SpringSecurity会从request中获取输入信息)
defaultSuccessUrl("/index"):登录成功后默认的访问地址。
failureForwardUrl("/loginerror"):登录失败后默认的访问地址。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
super.configure(auth);
}
@Override
public void configure(WebSecurity web) throws Exception {
super.configure(web);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//拦截并认证所有的请求
http.authorizeRequests()
//对于登录接口或登录页面不拦截
.antMatchers("/login","/mlogin.html").permitAll()
//所有的请求必须经过认证(包括登录),除非加入上面的不拦截
.anyRequest().authenticated()
//再返回一个HttpSecurity http
.and()
//设置登录页面
.formLogin().loginPage("/mlogin.html")
//登录页面填写完成后的提交地址,默认是/login(SpringSecurity已经默认实现了此接口)
.loginProcessingUrl("/login")
//登录页面form表单中用户名框对应的name
.usernameParameter("username")
//登录页面form表单中密码框对应的name
.passwordParameter("password")
//登录成功后访问
.defaultSuccessUrl("/index")
//登录失败后访问
.failureForwardUrl("/loginerror");
//关闭跨域攻击
http.csrf().disable();
}
}
用到的Controller如下。可以直接通过SpringContext上下文获取用户信息Authentication authentication = SecurityContextHolder.getContext().getAuthentication()。
@Controller
public class LoginController {
@RequestMapping(value = {"/","/index"})
@ResponseBody
public String index() {
String a = "index";
System.out.println(a);
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return a;
}
@RequestMapping("/loginerror")
@ResponseBody
public String loginerror() {
String a = "login error";
System.out.println(a);
return a;
}
}
测试结果分析:
-
访问http://localhost:8080/index,因为/index拦截,未通过认证,因此跳转到http://localhost:8080/mlogin.html【loginPage配置】。
-
输入错误的用户名密码,请求/loginerror【failureForwardUrl】,结果为login error。
-
输入正确的用户名密码aaa/123456,发现实际*问了/login接口【loginProcessingUrl】,认证成功后访问了/index接口【defaultSuccessUrl】,结果为/index。
静态权限设置
通过配置antMatchers.hasRole("")、hasAnyRole("","",...)、hasAuthority("")、hasAnyAuthority("","",...)来设定资源需要某角色或权限方可访问。
如下面代码中,设定/role的访问必须要用r1角色,设定/perm的访问必须要用p1权限。
//对于登录接口或登录页面不拦截
.antMatchers("/login","/mlogin.html").permitAll()
//对于接口访问必须需要某角色
.antMatchers("/role").hasRole("r1")
//对于接口访问必须需要某权限
.antMatchers("/perm").hasAuthority("p1")
测试接口如下。
@RequestMapping("/role")
@ResponseBody
public String role() {
String a = "role";
System.out.println(a);
return a;
}
@RequestMapping("/perm")
@ResponseBody
public String perm() {
String a = "perm";
System.out.println(a);
return a;
}
测试结果分析:
-
输入正确的用户名密码aaa/123456,结果为/index。
-
访问http://localhost:8080/perm或http://localhost:8080/role,此时显示type=Forbidden, status=403即无权限。因为尚未对用户aaa设定角色和权限,因此无法访问。
下一章,我们会将介绍加密方法和静态用户权限配置。