一. 简介
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架,致力于为Java应用程序提供身份验证和授权。
- 认证 (你是谁)
- 授权 (你的权限大小)
- 攻击防护 (防止身份伪造和网络攻击)
Spring Security采用的是责任链的设计模式,它有一条很长的过滤器链,项目启动后将会自动配置。最核心的是 Basic Authentication Filter 来认证用户的身份。
二. Spring Security入门
快速实现:不同的用户拥有不同的权限,不同的权限可以访问不同的网页
项目依赖中导入Spring Security启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
定义SecurityConfiguration 配置类,添加@EnableWebSecurity使能注解,继承WebSecurityConfigurerAdapter
- 首页和登录页不需要任何权限(SecurityConfiguration中定义)
- 用户页面需要USER权限(SecurityConfiguration中定义)
- 管理员页面需要ADMIN权限(SecurityConfiguration中定义)
- 如果用户没有登录,则访问需要权限的页面时自动跳转登录页面(SecurityConfiguration中定义)
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 在内存中创建一个名为 "user" 的用户,密码为 "pwd",拥有 "USER" 权限,密码使用BCryptPasswordEncoder加密
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER");
/**
* 在内存中创建一个名为 "admin" 的用户,密码为 "pwd",拥有 "USER" 和"ADMIN"权限
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN");
}
/**
* 匹配 "/","/index" 路径,不需要权限即可访问
* 匹配 "/user" 及其以下所有路径,都需要 "USER" 权限
* 匹配 "/admin" 及其以下所有路径,都需要 "ADMIN" 权限
* 登录地址为 "/login",登录成功默认跳转到页面 "/user"
* 退出登录的地址为 "/logout",退出成功后跳转到页面 "/login"
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/","/index","/error").permitAll()
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
.and()
.formLogin().loginPage("/login").defaultSuccessUrl("/user")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
}
}
该方法在内存中建立了两个用于验证的User对象,每当用户从网页登录时,与内存中的对象进行比较,如果用户名密码都匹配,则验证通过,否则不通过。值得注意的是,从SpringSecurity5.0以后,这种方式的密码都要使用passwordEncoder进行加密,否则就会报错.
任何一个权限管理系统,主要都分为两个功能:验证和鉴权。这里涉及到三个方面:用户(auth)、权限/角色(roles)、受保护的资源(resources)。
3. 整合MyBatis
3.1 建立用户权限表
CREATE DATABASE `mysecurity` DEFAULT CHARACTER SET utf8;
USE `mysecurity`;
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(32) DEFAULT NULL COMMENT '姓名',
`address` varchar(64) DEFAULT NULL COMMENT '联系地址',
`username` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '账号',
`password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码',
`roles` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL COMMENT '身份',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of `user`
-- ----------------------------
BEGIN;
INSERT INTO `user` VALUES ('1', 'Adam', 'beijing', 'adam','$2a$10$9SIFu8l8asZUKxtwqrJM5ujhWarz/PMnTX44wXNsBHfpJMakWw3M6', 'ROLE_USER');
INSERT INTO `user` VALUES ('2', 'SuperMan', 'shanghang', 'super','$2a$10$9SIFu8l8asZUKxtwqrJM5ujhWarz/PMnTX44wXNsBHfpJMakWw3M6', 'ROLE_USER,ROLE_ADMIN');
COMMIT;
3.2 MyBatis实现MyUserBean的mapper接口
@Mapper
@Component
public interface MyUserMapper {
/**
* 从数据库中查询用户
*/
@Select("select * from user where username = #{username}")
MyUserBean selectByUsername(@Param("username") String username);
}
3.3 MyUserDetailsService实现
@Service
public class MyUserDetailsService implements UserDetailsService {
@Autowired
MyUserMapper mapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
MyUserBean userBean = mapper.selectByUsername(username);
if (userBean == null) {
throw new UsernameNotFoundException("数据库中无此用户!");
}
return userBean;
}
}
3.4 SecurityConfiguration类调用myUserDetailsService
@Autowired
BackdoorAuthenticationProvider backdoorAuthenticationProvider;
@Autowired
MyUserDetailsService myUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/**
* 在内存中创建一个名为 "user" 的用户,密码为 "pwd",拥有 "USER" 权限,密码使用BCryptPasswordEncoder加密
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("user").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER");
/**
* 在内存中创建一个名为 "admin" 的用户,密码为 "pwd",拥有 "USER" 和"ADMIN"权限
*/
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("pwd")).roles("USER","ADMIN");
//将自定义验证类注册进去
auth.authenticationProvider(backdoorAuthenticationProvider);
//加入数据库验证类,下面的语句实际上在验证链中加入了一个DaoAuthenticationProvider
auth.userDetailsService(myUserDetailsService).passwordEncoder(new BCryptPasswordEncoder());
}
后序:
- 验证成功后,验证信息保存在哪里?在程序中如何获取这些信息?
- 怎么管理cookie?验证信息多久过期?
- 真正的系统中角色和用户应该能够动态调整,鉴权规则也很复杂,这怎么定制?
- 验证、鉴权的流程是怎么实现的?说好的filter链呢?说好的依赖注入、AOP呢?
源码探析将会在后面的学习中继续,感谢阅读。