写在前面:本博客是我初步学习security后的学习记录,博客以“简洁,实用,易于理解”为主,不会写过多“无聊”的篇幅。
1.对security的理解:
security就是做用户认证、用户授权和路径访问权限的一个安全框架。
- 用户认证authentic:即判断当前用户是不是合法用户(合法的含义:数据库中是否包含该用户信息)。
- 用户授权authority:将权限(实际就是角色)赋予给用户,注意数据库中的用户角色的关系只是一堆数据,没有任何意义(人能看懂,但 是机器/程序不行,所以要在程序中使角色和用户真正关联起来,即所谓的授权)。
- 路径访问权限:设置什么路径需要什么角色访问。
注意:authentic和authority这两个词。一个是认证一个是授权,记不住的同学可以想想“认证”的中国拼音“renzheng”
2.security是如何实现安全的?
实际上是一堆的拦截器。security导入项目后会自动创建一个FilterChainProxy的对象,该对象存在一个拦截器列表,顾名思义就是拦截各种请求,然后做安全判断(安全判断自己体会啥意思)。
3.首先导入security的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
springboot中已经集成了security,所以会采用默认版本(不知道啥意思的同学,去找找maven的知识点)
导入依赖后,就自动开启了安全机制,如果此时运行项目,security会自动创建一个合法用户(用户名:test , 密码:随机生成)
随机生成的密码可在控制台看见。
然而这自动创建的用户肯定不满足我的需求。我希望使用数据库中的合法用户。
4.首先看下我的项目结构和security:
5.首先需要写个security的配置类并继承WebSecurityConfigurerAdapter类(这样securiyt才会使用自定义的配置),通过这个配置类,我可以自定义用户认证、授权、规定路径访问权限。
@EnableWebSecurity //开启security功能
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //该方法用来做用户的认证和授权
//密码编码器,security强制规定必须有个密码编码器,对传进来的密码编码
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//需要绑定自定义认证服务和密码编码器,参数userDetailsService是一个用户详情服务类
//security就是根据这个服务类对用户进行认证和授权的。
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
}
@Override
protected void configure(HttpSecurity http) throws Exception { //该方法用来做路径的访问权限
http.authorizeRequests() //固定写法
.antMatchers("/test/insertUser").permitAll() //antMatchers方法匹配路径,permitAll表示任何用户都能方法(不需用户认证)
.antMatchers("/test/print").hasRole("ADMIN") //hasRole("ADMIN")表示只有角色admin有权限访问
.anyRequest().authenticated() //anyRequest表示其他任何请求,authenticated表示需要认证
.and() //过渡作用,返回一个对象(与http.authorizeRequests()返回的对象相同)
.formLogin(); //采用security默认的登陆页面
}
@Override
public void configure(WebSecurity web) {
super.configure(web);
}
}
6.用户详情服务类UserDetailsService
第5步中的用户认证和授权方法中,绑定了自定义的用户认证和授权的服务类
这个服务类(UserDetailsService)的作用就是返回一个用户详情对象UserDetails(包含用户名、密码、和权限列表(实际就是角色列表,只不进行了封装))。
然后再由auth.userDetailsService()这个方法做安全判断。
而security默认提供了一个用户详情服务类接口UserDetailsService,需要我们自定义一个用户详情服务类并实现该接口
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService; //自定义的服务,作用:可从数据库MySQL,redis或内存中查询用户
@Autowired
private RoleService roleService;
@Override
@Transactional
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
User user = userService.selectUserByName(s); //根据用户名查询用户信息(用户名是前端传入)
List<Role> roleList = roleService.selectRoleByName(s); //根据用户名查询角色信息
List<GrantedAuthority> authorityList = new ArrayList<>(); //GrantedAuthority权限类
for(Role role: roleList){ //将角色列表转化为权限列表
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
authorityList.add(grantedAuthority);
}
UserDetails userDetails = new org.springframework.security.core.userdetails.User(user.getUserName(), user.getPassword(), authorityList); //生成用户详情对象
return userDetails;
}
}
注意:
7.特别说明:
- security会自动获取前端登陆页面传入的用户名和密码
- 数据库中的密码必须是经密码编码器编码过后的密码,不能是明文密码。因为传进的密码会被编码器编码再与数据库中查出来的密码做比较
- 自定义认证和授权的关键是:需要定义一个服务类实现接口UserDetailsService,并重写唯一的方法(返回值是一个用户详情类UserDetails)
- 除了自定义用户认证和授权,还可以采用security提供的内存认证和授权(测试专用)、数据库认证和授权(MySQL)。只是实际开发采用自定义用户认证的方式比较多。