- Spring Security概念
- Spring Boot对Spring Security的支持
- 企业Spring Security操作
Spring Security是一个强大且高度可定制的身份验证和访问控制框架
- Spring Security概念
为基于Spring的企业应用系统提供安全访问控制解决方案的安全框架
安全框架主要包括2个操作:
认证:确认用户可以访问当前系统
授权:确定用户在当前系统中是否能够执行某个操作,即用户所拥有的功能权限
Spring Security包括多个模块:
核心模块(spring-security-core.jar):任何使用Spring Security的应用程序都需要这个模块
基于annotation注解来完成Spring Security的功能
- Spring Boot对Spring Security的支持
(1)Security适配器
在Spring Boot中配置Spring Security非常简单,创建一个自定义类继承WebSecurityConfigurerAdapter,并在该类中使用@EnableWebSecurity注解,就可以通过重写config方法来配置所需要的安全配置
WebSecurityConfigurerAdapter是Spring Security为Web应用提供的一个适配器,提供了2个方法:
configure(HttpSecurity httpSecurity):定义哪些URL需要被保护,定义当需要用户登录时,跳转到的登录页面
configureGlobal(AuthenticationManagerBuilder auth):用于创建用户和用户的角色
(2)用户认证
通过在configureGlobal(AuthenticationManagerBuilder auth)完成用户认证的
使用AuthenticationManagerBuilder的inMemoryAuthentication()方法可以添加用户,并给用户指定权限:
auth.inMemoryAuthentication().withUser("fkit").password("123456").roles("ADMIN","DBA");
Spring Security保存用户权限时,默认使用“ROLE_”,也就是说,“ADMIN”实际上是“ROLE_ADMIN”
(3)用户授权
通过configure(HttpSecurity http)完成用户授权的
HttpSecurity的authorizeRequests()方法有多个子节点,指定用户可以访问的多个URL模式
http.authorizeRequests():开始请求权限匹配
anyRequest().authenticated():其余所有的请求都需要认证(用户登录)之后才可以访问
HttpSecurity还可以设置登录的行为:
formLogin():开始设置登录操作
loginPage("/login"):设置登录页面的访问地址
usernameParameter("loginName").passwordParameter("password"):登录时接收传递的参数"loginName"的值作为用户名,接收传递的参数"password"的值作为密码
exceptionHandling().accessDeniedPage("/accessDenied"):指定异常处理页面
(4)Spring Security核心类
Authentication:用户认证信息,Authentication对象
SecurityContextHolder:保存SecurityContext,含有当前所访问系统的用户的详细信息,获取当前登录用户的用户名:
String username = SecurityContextHolder.getContext().getAuthentication().getName();
UserDetails:定义了一些可以获取用户名、密码、权限等与认证相关的信息的方法,UserDetails是通过UserDetailsService的loadUserByUsername()方法进行加载的
UserDetailsService:Spring Security通过UserDetailsService的loadUserByUsername()方法获取对应的UserDetails进行认证,认证通过后会将该UserDetails赋给认证通过的Authentication的principal,然后再把该Authentication存入SecurityContext,之后如果需要使用用户信息,可以通过SecurityContextHolder获取存放在SecurityContext中的Authentication的principal
GrantedAuthority:Authentication的getAuthorities()可以返回当前Authentication对象拥有的权限,返回值是一个GrantedAuthority类型的数组,通常通过UserDetailsService进行加载,然后赋予UserDetails的
DaoAuthenticationProvider:默认使用DaoAuthenticationProvider实现AuthenticationProvider接口,专门进行用户认证的处理
PasswordEncoder:对密码加密,BCryptPasswordEncoder是较好的选择,可以由客户端指定加密的强度
(5)Spring Security的验证机制
Spring Security大体上是由一堆Filter实现的,Filter会在Spring MVC前拦截请求,Filter包括登出Filter、用户名密码验证Filter之类,Filter再交由其他组件完成细分的功能
(6)Spring Boot的支持
Spring Boot针对Spring Security提供了自动配置的功能
通过org.springframework.boot.autoconfigure.security包对Spring Security提供了自动配置的支持,主要通过SecurityAutoConfiguration和SecurityProperties两个类来完成
SecurityProperties类使用以“security”为前缀的属性配置Spring Security相关的配置
当需要自己扩展时,只需要自定义类继承WebSecurityConfigurerAdapter,无须再使用@EnableWebSecurity注解
Spring Boot自动注册Security的过滤器
Spring Boot Security示例:
1、修改pom.xml文件,引入依赖spring-boot-starter-security
2、html页面
3、Spring Security认证处理类:AppSecurityConfigurer.java,用于用户认证和授权操作
@Configuration
public class AppSecurityConfigurer extends WebSecurityConfigurerAdapter {……}
密码存储格式为:{id}encodedPassword,id是一个标识符,用于查找是哪个PasswordEncoder,encodedPassword是经过加密之后的密码,设置密码的示例代码如下:
auth.inMemoryAuthentication().withUser("fkit").password("{noop}123456").roles("USER");
Spring Security还提供了密码编辑器接口PasswordEncoder,自定义密码编辑器类必须实现PasswordEncoder接口
注入认证成功处理类:
AppAuthenticationSuccessHandler appAuthenticationSuccessHandler;
.successHandler(appAuthenticationSuccessHandler);
4、创建认证成功处理类:AppAuthenticationSuccessHandler.java,用于处理登录成功之后的操作
@Component
public class AppAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {......}
通过RedirectStrategy对象负责所有重定向任务
private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
重写handle方法,方法中通过RedirectStrategy对象重定向到指定的URL
从Authentication对象中提取当前登录用户的角色,并根据其角色返回适当的URL
redirectStrategy.sendRedirect(request,response,targetUrl);
5、转发请求的控制器
@Controller
public class AppController {……}
注销:new SecurityContextLogoutHandler().logout(request,response,auth);
通过getUsername()方法获得当前认证用户的用户名,通过getAuthority()方法获得当前认证用户的权限,并设置到Model中
6、测试应用
Spring Boot项目启动后,就调用了AppSecurityConfigurer.java
- 企业Spring Security操作
操作企业数据库主流的方式有JPA、MyBatis、JDBC,通过验证登录名loginName和密码password进行认证并授权,同时密码使用加密处理,避免将密码明文存储到数据库
(1)基于JPA的Spring Boot Security操作
创建用户类和角色类
FKRole.java:
@Column(name="id")
private Long id;
@Column(name="authority")
private String authority;
FKUser.java:
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
@JoinTable(name="tb_user_role",joinColumns={@JoinColumn(name="user_id")},inverseJoinColumns={@JoinColumn(name="role_id")})
private List<FKRole> roles;
用户和权限的关系是多对多关系
创建数据库访问接口:UserRepository.java
创建自定义服务类:UserService.java
需要实现UserDetailsService接口,因为在Spring Security中配置的相关参数需要是UserDetailsService类型的数据
public class UserService implements UserDetailsService {……}
重写UserDetailsService接口中的loadUserByUsername方法,通过该方法查询对应的用户,返回对象UserDetails是Spring Security的一个核心接口
其中调用持久层接口findByLoginName方法查找用户,通过JPA进行数据库验证
FKUser fkuser = userRepository.findByLoginName(username);
最后将获得的用户名、密码和权限保存到org.springframework.security.core.userdetails.User类中并返回
创建Spring Security的认证处理类AppSecurityConfigurer.java
依赖注入用户认证接口
private AuthenticationProvider authenticationProvider;
DaoAuthenticationProvider是Spring Security提供的AuthenticationProvider实现
通过重写configureGlobal方法添加自定义的认证方式
@Override
protected void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Bean
public Authentication authentication()
{
……
provider.setUserDetailsService(userService);
provider.setPasswordEncoder(passwordEncoder);
returen provider;
}
.anyRequest().authenticated()表示其他的请求都必须要有权限认证
使用AuthenticationManagerBuilder的authenticationProvider()方法进行认证,在authenticationProvider()方法中调用setUserDetailsService()方法传入自定义的UserService对象进行用户登录认证,认证通过后会将UserDetails赋给认证通过的Authentication的principal,然后再把该Authentication存入SecurityContext
在authenticationProvider()方法中调用setPasswordEncoder()方法传入Spring Security提供的BCryptPasswordEncoder对象进行密码加密验证(对输入的密码加密,再比对此加密后的密码与数据库中密码是否相等)
(2)基于MyBatis的Spring Boot Security操作
持久层使用的是MyBatis框架
Spring Boot会自动加载spring.datasource.*相关配置,数据源就会自动注入sqlSessionFactory,sqlSessionFactory会自动注入Mapper
创建数据访问接口UserMapper.java,和JPA测试中的UserRepository作用完全一致,区别在于JPA自动生成SQL语句,而MyBatis需要开发者自动提供SQL语句
修改App类:
@MapperScan("org.fkit.securitymybatistest.mapper") //扫描数据访问层接口的包名
作用是让Spring能够扫描该包下面所有MyBatis的Mapper类,还有一种方式是直接在Mapper类中添加注解@Mapper
(3)基于JDBC的Spring Boot Security操作
持久层使用JDBC操作数据库
创建数据访问接口UserRepository.java,注入JdbcTemplate:
@Autowired
private JdbcTemplate jdbcTemplate;
//根据loginName查询用户
FKUser fkUser = jdbcTemplate.queryForObject(sql,new Object[]{loginName},new RowMapper<FKUser>(){
@Override
public FKUser mapRow(ResultSet rs,int rowNum) throws SQLException{……}
}
);
//根据用户id查询用户权限
List<Map<String,Object>> result = jdbcTemplate.queryForList("……",new Object[]{fkUser.getid()});
JDBC需要开发者自己提供SQL语句并处理结果集,其他的工作都交给JdbcTemplate对象完成
JPA和MyBatis的Spring Security操作是现代开发的主流