前言
学习security,记录下
正文
1. 简介
Spring Security是为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了完整的安全性解决方案,可以在Web请求级别和方法调用级别处理身份认证和授权充分利用了Spring IOC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能。
2. 模块
- ACL 支持通过访问控制列表(access control list,ACl)为域对象提供安全性
- Aspects 一个很小的模块,当使用Spring Security注解时,会使用基于AspectJ的切面
- CAS Client 提供与CAS集成的功能
- Configuration 包含XML和JAVA配置的功能支持
- Core 基本功能库
- Cryptography 提供加密和密码编码相关功能
- LDAP 基于LDAP认证
- OpenID 支持使用OpenID进行集中式认证
- Remoting 提供了对Spring Remoting的支持
- Tag Library Security的JSP标签库
- Web 提供了Security基于Filter的Wen安全性支持
备注:
应用程序的类路径下至少需要包括Core和Configuration模块,Web应用下需要加入Web应用
3. Demo(基于SpringBoot+SpringSecurity+MySql)
3.1. pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<!--支持热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<executable>true</executable>
<fork>true</fork><!--注意要修改这里,支持热部署-->
</configuration>
</plugin>
</plugins>
</build>
3.2. config
配置类WebSecurityConfig继承WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity//启用安全
@EnableGlobalMethodSecurity(securedEnabled = true,jsr250Enabled = true,prePostEnabled = true)//开启注解
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
重载configure(HttpSecurity)方法,通过重载,配置如何通过拦截器保护请求
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin().loginPage("/login").loginProcessingUrl("/auth/login").defaultSuccessUrl("/index")//指定登录页面以及登录成功后的跳转页面
.and()
.logout().logoutSuccessUrl("/login")//指定登出后的页面
.and()
.authorizeRequests()//拦截请求配置
.antMatchers("/css/**", "/js/**","/images/**", "/static/**").permitAll()//允许访问静态资源
.antMatchers("/login","/auth/login").permitAll() // premitAll()不做拦截
.antMatchers("/index").authenticated() //authenticated() 必须登录后可见
.antMatchers("/hello").access("hasRole('ADMIN')") //必须拥有ADMIN角色权限才可以访问
.anyRequest().authenticated();
//.and()
// .rememberMe().tokenValiditySeconds(2419200).tokenRepository(persistentTokenRepository()).alwaysRemember(true);// 启用记住我
}
重载configure(AuthenticationManagerBuilder)方法,通过重载,配置user-detail服务
重载configure(WebSecurity)方法,通过重载,配置Security的Filter链
3.3. 四种用户验证方式
上面提到了重载configure(AuthenticationManagerBuilder)方法,可以配置user-detail服务,这个方法决定用户的校验方式,一般来说有四种
- 基于内存验证
auth.inMemoryAuthentication()
.withUser("admin").password(passwordEncoder().encode("admin")).roles("ADMIN").and()
.withUser("test").password(passwordEncoder().encode("test")).roles("TEST");
- 基于数据库验证
auth.jdbcAuthentication().dataSource(dataSource)
.usersByUsernameQuery("select username,password,enabled from user where username = ?")
.authoritiesByUsernameQuery("select username,rolename from role where username=?")
.passwordEncoder(passwordEncoder());
- 基于LDAP验证
auth.ldapAuthentication().userSearchFilter("{uid=0}").groupSearchFilter("member={0}");
- 自定义用户服务
auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
这里customUserDetailsService必须实现UserDetailsService
@Component
@Slf4j
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByAccount(username);
if (user==null){
throw new AuthenticationCredentialsNotFoundException("authError");
}
Set<Role> roles = user.getRoles();
List<GrantedAuthority> authorities = new ArrayList<>();
roles.forEach(role-> authorities.addAll(AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_"+role.getName())));
return new org.springframework.security.core.userdetails.User(user.getAccount(),user.getPassword(),authorities);
}
}
3.4. 注解
三种不同的安全注解
- 自带的@Secured注解(@EnableGlobalMethodSecurity中设置securedEnabled = true)
- JSR-250的@RolesAllowed注解(@EnableGlobalMethodSecurity中设置jsr250Enabled = true)
-
表达式驱动注解,@PreAuthorize、@PostAuthorize、@PreFilter、@PostFilter
这里我们主要讲表达式驱动注解:
@PreAuthorize 方法调用之前,基于表达式的计算结果来限制对方法的访问
@PostAuthorize 方法调用之后,如果表达式的结果为false,则抛出异常
@PreFilter 方法调用之前,过滤进入方法的输入值
@PostFilter 方法调用之后,过滤方法的结果值
@Controller
@RequestMapping("/")
public class AuthController {
@GetMapping("login")
public String login (){
return "login";
}
@GetMapping("index")
public String index(){
return "index";
}
@GetMapping("hello")
@PreAuthorize("hasRole('ADMIN')")
public String hello(){//必须拥有ADMIN权限的用户才可以进去
return "hello";
}
@GetMapping("postAuth")
@PostAuthorize("hasRole('TEST')")
@ResponseBody
public String postAuth(){//可以进入但是没有TEST的权限的用户会报403错误
return "postAuth";
}
@PostMapping("preFilter")
@PreFilter(filterTarget="users", value="filterObject.id != null")
@ResponseBody
public String preFilter(@RequestBody List<User> users){//进入方法后users的size为0
return "preFilter";
}
@PostMapping("postFilter")
@PostFilter(value="filterObject.id != null")
@ResponseBody
public List<User> postFilter(@RequestBody User user){//客户端拿到的是空的
List<User> users = new ArrayList<User>();
users.add(user);
return users;
}
}
总结
一些基本的功能以及介绍已经了解了,具体的使用还是需要投入到实际项目中,下面接着往下学习。
security是如何进行登录验证和权限控制的?
参考资料
- Spring In Action 第四版 9章和14章
- Security 官方文档
- Security 参考手册