概述:
学习一个简单demo,会有很多疑问需要解决,是时候梳理一下概念。
1、SpringSecurity 模块组成
Spring Security 3.2 分为 11个模块
ACL:支持通过访问控制列表(access Control list, ACL)为域对象提供安全
Aspects(切面): 使用Spring Security注解时,会使用基于AspectJ的切面,而不是标准的SpringAOP
CAS Client :提供与Jasig的中心认证服务(Central Authentication Service, CAS)进行集成的功能
Configuration: 包含通过xml和java配置Spring Security 的功能支持
Core : 提供Spring Security基本库
Cryptography(加密) : 提供了加密和密码编码的功能
LDAP :支持基于LDAP进行认证
OpenID : 支持使用OpenID进行集中式认证
Remoting: 提供了对Spring Remoting 的支持
Tag Library: Spring Security的JSP标签库
Web : 提供了Spring Security基本Filter的Web安全性支持
1.2 过滤Web 请求
安全通过过滤来拦截验证,是否需要配置多个过滤器,其实只要配置一个代理过滤,它会自动找具体实现过滤器
方式一
<filter >
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>
org.springframework.web.filter.DelegatingFilterProxy
</filter-class>
</filer>
方式二:
实现AbstractSecurityWebApplicationInitializer父类
package com.jack.config;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer{
}
1.3 Spring Security 配置 继承WebSecurityConfigurerAdapter
启动安全注解 @EnableWebSecurity
@Configuration
@EnableWebSecurity // 启动web安全控制
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.inMemoryAuthentication().withUser("jack").password("123456").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("123456").roles("ADMIN");
auth.inMemoryAuthentication().withUser("dba").password("123456").roles("DBA");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')")
.and().formLogin();
}
}
重写configure入参不同,如下所示
WebSecurity、HttpSecurity、AuthenticationManagerBuilder
配置用户详细信息的方法
accountExpired(boolean) 定义账号是否已经过期
accountLocked(boolean) 定义账号是否已经锁定
and() 用来连接配置
authorities(GrantedAuthority...) 授予某个用户一项或多项权限
authorities(List <? extends GrantedAuthority>) 授予某个用户一项或多项权限
authorities(String ...) 授予某个用户一项或多项权限
credentialsExpired(boolean) 定义凭证是否已经过期
disabled(boolean) 定义账号是否已被禁用
password(String) 定义用户密码
roles(String...) 授予某个用户的一项或多项角色
1.4、基于数据库表进行认证
用到方法是 jdbcAuthentication()
@Autowired
DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth.jdbcAuthentication().dataSource(dataSource);
}
自定查询语句
@Autowired
DataSource dataSource;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception{
auth
.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username, password, true "+
"from Spitter where username=?")
.authoritiesByUsernameQuery(
"select username,'ROLE_USER' from Spitter where username=?")
.passwordEncoder(new StandardPasswordEncoder("532222"));
}
1.5、基于LDAP 服务器
auth.ldapAuthentication()
.userSearchBase("ou=people")
.userSearchFilter("(uid={0})")
.groupSearchBase("ou=groups")
.groupSearchFilter("member={0}")
.passwordCompare()
.passwordEncoder(new Md5PasswordEncoder())
.passwordAttribute("passcode");
如果是远程服务。通过contextSource().url("输入地址");
auth.ldapAuthentication()
.userSearchBase("ou=people")
.userSearchFilter("(uid={0})")
.groupSearchBase("ou=groups")
.groupSearchFilter("member={0}")
.contextSource()
.url("ldap://网址")
1.6 非关系型数据库,可以实现 UserDetailsService 接口
重写loadUserByUsername(String username) 方法
package com.jack.data;
import java.util.ArrayList;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
public class SpitterUserService implements UserDetailsService {
private final SpitterRepository spitterRepository;
public SpitterUserService(SpitterRepository spitterRepository) {
this.spitterRepository = spitterRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Spitter spitter = spitterRepository.findByUsername(username);
if(spitter != null){
List<GrantedAuthority> authorities =
new ArrayList <GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority("ROLE_SPITTER"));
return new User(spitter.getUsername(),
spitter.getPassword(),
authorities);
}
throw new UsernameNotFoundException("User '"
+ "username " + "' not found");
}
}
1、不关心数据如何来,只要返回一个User对象就行
配置 auth.userDetailsService(new SpitterUserService(spitterRepository));
1.7 拦截请求
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
.antMatchers("/dba/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_DBA')")
.and().formLogin();
}
antMatchers() 是Ant风格,也支持regex风格
.regexMatchers()
用来定义如何保护路径的配置方法
access(String) 如果给定的SpEL表达式计算结果为true, 就允许访问
anonymous() 允许匿名用户访问
authenticated() 允许认证过的用户访问
denyAll() 无条件拒绝所有访问
fullyAuthenticated() 如果用户是完整的话(不是通过Remember-me 功能认证的),就能访问
hasAnyAuthority(String...) 如果用户具备给定权限中的某一个的话,就允许访问
hasAnyRole(String...) 如果用户具备给定角色中的某一个的话,就允许访问
hasAuthority(String) 如果用户具备给定权限的话,就允许访问
hasIpAddress(String) 如果请求来自给定IP地址的话,就允许访问
hasRole(String) 如果用户具备给定角色的话,就允许访问
not() 对其他访问方法的结果取反
permitAll () 无条件允许访问
rememberMe() 如果用户是通过Remember-me功能认证的,就允许访问
1.8 强制通道的安全性
HTTPS 加密发送
1.9 防止跨站请求伪造
Cross-site request forgery (CSRF)
通过同步token来解决, 也就是在表单中包含_csrf域
总结:
Spring-Security 可以完成很多验证相关的事情,具体需要慢慢研究