前言
学习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
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-test
test
mysql
mysql-connector-java
org.springframework.boot
spring-boot-starter-data-jpa
org.projectlombok
lombok
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.thymeleaf.extras
thymeleaf-extras-springsecurity4
org.springframework.boot
spring-boot-devtools
true
org.springframework.boot
spring-boot-maven-plugin
true
true
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 roles = user.getRoles();
List 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 users){//进入方法后users的size为0
return "preFilter";
}
@PostMapping("postFilter")
@PostFilter(value="filterObject.id != null")
@ResponseBody
public List postFilter(@RequestBody User user){//客户端拿到的是空的
List users = new ArrayList();
users.add(user);
return users;
}
}
总结
一些基本的功能以及介绍已经了解了,具体的使用还是需要投入到实际项目中,下面接着往下学习。
security是如何进行登录验证和权限控制的?
参考资料
- Spring In Action 第四版 9章和14章
- Security 官方文档
- Security 参考手册
参考代码
demo 地址