Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求
授权
所谓的授权,就是用户如果要访问某一个资源,我们要去检查用户是否具备这样的权限,如果具备就允许访问,如果不具备,则不允许访问。
准备测试用户
因为现在还没有连接数据库,所以测试用户还是基于内存来配置。
基于内存配置测试用户,有两种方式,第一种就配置方式,如下:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("yuzb")
.password("123").roles("admin")
.and().
withUser("yu")
.password("123").roles("user");
}
由于 Spring Security 支持多种数据源,例如内存、数据库、LDAP 等,这些不同来源的数据被共同封装成了一个 UserDetailService 接口,任何实现了该接口的对象都可以作为认证数据源。
因此还可以通过重写 WebSecurityConfigurerAdapter 中的 userDetailsService 方法来提供一个 UserDetailService 实例进而配置多个用户:
@Bean
protected UserDetailsService userDetailsService() {
InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
manager.createUser(User.withUsername("a").password("123").roles("admin").build());
manager.createUser(User.withUsername("b").password("123").roles("user").build());
return manager;
}
以上任选其一
准备测试接口
@RestController
public class SecurityController { @GetMapping("/hello")
public String hello() {
return "hello";
}
@GetMapping("/admin/hello")
public String admin() {
return "admin";
}
@GetMapping("/user/hello")
public String user() {
return "user";
}
}
这三个测试接口规划是这样的:
- /hello 是任何人都可以访问的接口
- /admin/hello 是具有 admin 身份的人才能访问的接口
- /user/hello 是具有 user 身份的人才能访问的接口
- 所有 user 能够访问的资源,admin 都能够访问
注意第四条规范意味着所有具备 admin 身份的人自动具备 user 身份。
配置
接下来来配置权限的拦截规则,在 Spring Security 的 configure(HttpSecurity http) 方法中,代码如下:
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.anyRequest().authenticated()
.and()
这里的匹配规则采用了 Ant 风格的路径匹配符,Ant 风格的路径匹配符在 Spring 家族中使用非常广泛,它的匹配规则也非常简单:
上面配置的含义是:
- 如果请求路径满足 /admin/** 格式,则用户需要具备 admin 角色。
- 如果请求路径满足 /user/** 格式,则用户需要具备 user 角色。
- 剩余的其他格式的请求路径,只需要认证(登录)后就可以访问。
注意代码中配置的三条规则的顺序非常重要,和 Shiro 类似,Spring Security 在匹配的时候也是按照从上往下的顺序来匹配,一旦匹配到了就不继续匹配了,「所以拦截规则的顺序不能写错」。
另一方面,如果强制将 anyRequest 配置在 antMatchers 前面,像下面这样:
http.authorizeRequests()
.anyRequest().authenticated()
.antMatchers("/admin/**").hasRole("admin")
.antMatchers("/user/**").hasRole("user")
.and()
此时项目在启动的时候,就会报错,会提示不能在 anyRequest 之后添加 antMatchers:
nested exception is java.lang.IllegalStateException: Can't configure antMatchers after anyRequest
启动项目,以user角色访问
- http://localhost:8080/hello因为登录后就可以访问,这个接口访问成功。
- http://localhost:8080/admin/hello需要 admin 身份,所以访问失败。
- http://localhost:8080/user/hello访需要 user 身份,所以访问成功。
角色继承
所有 user 能够访问的资源,admin 都能够访问,很明显目前的代码还不具备这样的功能。
要实现所有 user 能够访问的资源,admin 都能够访问,这涉及到另外一个知识点,叫做角色继承。
这在实际开发中非常有用。
上级可能具备下级的所有权限,如果使用角色继承,这个功能就很好实现,只需要在 SecurityConfig 中添加如下代码来配置角色继承关系即可:
@Bean
RoleHierarchy roleHierarchy() {
RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
hierarchy.setHierarchy("ROLE_admin > ROLE_user");
return hierarchy;
}
注意,在配置时,需要给角色手动加上 ROLE_ 前缀。上面的配置表示 ROLE_admin 自动具备 ROLE_user 的权限。
访问http://localhost:8080/user/hello,发现admin角色可以访问user’角色权限的接口
将用户数据存入数据库
加入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
用户配置
@Autowired
DataSource dataSource;
@Bean
protected UserDetailsService userDetailsService() {
// InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
JdbcUserDetailsManager manager = new JdbcUserDetailsManager();
manager.setDataSource(dataSource);
if (!manager.userExists("a")) {
manager.createUser(User.withUsername("a").password("123").roles("admin").build());
}
if (!manager.userExists("b")) {
manager.createUser(User.withUsername("b").password("123").roles("user").build());
}
return manager;
}
数据库配置
spring.datasource.username=root
spring.datasource.password=yuzb
spring.datasource.url=jdbc:mysql:///yuzbadmin?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
执行数据库脚本
CREATE TABLE users(username VARCHAR(50) NOT NULL PRIMARY KEY,PASSWORD VARCHAR(500) NOT NULL,enabled BOOLEAN NOT NULL);
CREATE TABLE authorities (username VARCHAR(50) NOT NULL,authority VARCHAR(50) NOT NULL,CONSTRAINT fk_authorities_users FOREIGN KEY(username) REFERENCES users(username));
CREATE UNIQUE INDEX ix_auth_username ON authorities (username,authority);
- users 表中保存用户的基本信息,包括用户名、用户密码以及账户是否可用。
- authorities 中保存了用户的角色。
- authorities 和 users 通过 username 关联起来。
启动项目查看数据库
这样就能正常访问了
修改数据库将用户的 enabled 属性设置为 false,表示禁用该账户,此时再使用该账户登录就会登录失败。