SpringSecurity介绍
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AsjxeAoK-1672023162095)(images/1583585891579.png)]
Spring Security是基于spring的应用程序提供声明式安全保护的安全性框架,它提供了完整的安全性解决方案,能够在web请求级别和方法调用级别处理身份证验证和授权,它充分使用了依赖注入和面向切面的技术。
Spring Security 的前身是 Acegi Security,后来成为了 Spring 在安全领域的顶级项目,并正式更名到 Spring 名下,成为 Spring 全家桶中的一员,所以 Spring Security 很容易地集成到基于 Spring 的应用中来。Spring Security 在用户认证方面支持众多主流认证标准,包括但不限于 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等,在用户授权方面,Spring Security 不仅支持最常用的基于 URL 的 Web 请求授权,还支持基于角色的访问控制(Role-Based Access Control,RBAC)以及访问控制列表(Access Control List,ACL)等。
Spring Security 是 Spring 家族中的一个安全管理框架,Spring Security 已经发展了多年了,但是使用的并不多,安全管理这个领域,一直是 Shiro 的天下。相对于 Shiro,在 SSM/SSH 中整合 Spring Security 都是比较麻烦的操作,所以,Spring Security 虽然功能比 Shiro 强大,但是使用反而没有 Shiro 多,Shiro 虽然功能没有 Spring Security 多,但是对于大部分项目而言,Shiro 也够用了。
SpringSecurity主要是从两个方面解决安全性问题:
web请求级别:使用servlet过滤器保护web请求并限制URL级别的访问
**方法调用级别:**使用Spring AOP保护方法调用,确保具有适当权限的用户才能调用方法.
权限相关概念
主体(principal)
使用系统的用户或设备或从其他系统远程登录的用户等等。认证(authentication)
权限管理系统确认一个主体的身份,允许主体进入系统。授权(authorization)
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
SpringSecurity主要功能
- 认证就是确定主体的过程,当未认证的主体访问系统资源的时候,系统会对主体的身份进行验证,确定该主体是否有合法的身份,不合法的主体将被应用拒绝访问,这一点也很容易理解,比如某电商网站,未登录的用户是无法访问敏感数据资源的,比如订单信息。
- 授权是在主体认证结束后,判断该认证主体是否有权限去访问某些资源,没有权限的访问将被系统拒绝,比如某电商网站的登录用户去查看其它用户的订单信息,很明显,系统会拒绝这样的无理要求。
- 攻击防护:防止攻击伪造
环境搭建
POM
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Controller
@Controller
public class UserController {
@RequestMapping(value = "/hello")
@ResponseBody
public String hello(){
System.out.println("UserController.hello");
return "Hello Sprign Security";
}
}
访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-G1h39Uvl-1672023162096)(images/1583592152841.png)]
Spring Boot 项目引入了 Spring Security 以后,自动装配了 Spring Security 的环境,Spring Security 的默认配置是要求经过了 HTTP Basic 认证成功后才可以访问到 URL 对应的资源,且默认的用户名是 user,密码是自动随机生成一串 UUID 字符串,输出到了控制台日志里,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EDny5Fxb-1672023162097)(images/1583592227948.png)]
身份认证的方式
配置文件
spring:
security:
user:
name: admin
password: 123
自定义实现类(常用)
通过UserDetailsService来查询用户的密码,最后把用户封装成一个user返回给SpringSecurity。
MyUserDetailsService
在
Spring Security
中,用户的登录操作都是通过 UserDetailsService完成的,它是一个接口,该接口中只有一个loadUserByUsername(),在用户登录的时候SpringSecurity就会调用这个方法,该方法返回一个UserDetails对象,这个对象就代表了当前认证的主体,需要从数据库中根据用户名查询,从而返回出去,SpringSecurity会把该方法的返回值放到Session中,如果验证失败这个出现异常会抛出一个UsernameNotFoundException。
@Configuration
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private BCryptPasswordEncoder passwordEncoder;
/**
* 根据用户名查询密码
* @param username 表单输入的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("s = [" + s + "]");
// 在这里可以根据用户名从数据库中查询密码
// User user = baseMapper.getUserByUsername(username);
//if(user == null){
// 如果用户名不存在直接抛出异常
// throw new UsernameNotFoundException("用户名不存在");
//}
// 密码加密
String password = passwordEncoder.encode("123");
// 权限
List<GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("admin");
// 返回user(用户名,密码,权限)
return new User("点toString",password,authorities);//SpringSecurity会返回值放到Session中
}
}
SpringSecurityConfig
在SpringSecurityConfig中设置使用UserDetailsService来加载用户的密码。
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
// spring security 官方推荐的是使用bcrypt加密方
// SpringSecurity在对表单密码加密的时候需要用到这个bean,所以这里需要给它创建。如果不创建会登录会报错。
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService) // 自定义实现类完成密码的设置
.passwordEncoder(passwordEncoder()); // 设置密码加密算法
}
}
自定义JSON登录
UsernamePasswordAuthenticationFilter是AbstractAuthenticationProcessingFilter针对使用用户名和密码进行身份验证而定制化的一个过滤器。其添加是在调用http.formLogin()时作用,默认的登录请求pattern为"/login",并且为POST请求。当我们登录的时候,也就是匹配到loginProcessingUrl,这个过滤器就会委托认证管理器authenticationManager来验证登录。
1、定义登录拦截器
@Component
public class LoginFilter extends UsernamePasswordAuthenticationFilter {
public LoginFilter() {
// 默认登录使用post提交
this.setPostOnly(false);
// 默认登录的urlloign
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/userLogin","GET"));
}
@Autowired
@Override
public void setAuthenticationManager(AuthenticationManager authenticationManager) {
super.setAuthenticationManager(authenticationManager);
}
// 登录成功调用
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
Object principal = authResult.getPrincipal();
Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
// 登录成功后把权限放入到本次会话
SecurityContextHolder.getContext().setAuthentication(authResult);
ResponceUtils.out(response, R.ok().put("msg","登录成功").put("token","000"));
}
// 登录失败调用
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
ResponceUtils.out(response, R.ok().put("msg","用户名获密码错误"));
}
}
2、配置登录拦截器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private LoginFilter loginFilter;
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling()
.and().addFilter(loginFilter); // 加入登录拦截器
}
}
用户注销
http.formLogin()
.loginPage("/toLogin")
.loginProcessingUrl("/login")
.defaultSuccessUrl("/toSuccess")
.usernameParameter("name")
.passwordParameter("pw")
.and()
.logout() // 添加注销处理
.logoutUrl("/toLogout") // 注销地址
.logoutSuccessUrl("http://www.baidu.com") // 注销成功后调整的地址
//退出处理器,可以写匿名内部类
.addLogoutHandler(new LogoutHandler() {
@Override
public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
}
})
// 设置该方法会覆盖logoutSuccessUrl()方法
// 退出成功处理器
.logoutSuccessHandler(new LogoutSuccessHandler() {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
}
}).and().csrf().disable();
权限控制
SpringSecurity中可以通过角色和权限进行权限的控制,主体登录成功对访问的资源进行判断是否有这个权限,如果有就执行,否则就会跳转到没有权限访问的页面。
Spring Security 中对于权限控制默认已经提供了很多了 , 其中有四种时常用的权限控制方式。
- 表达式控制 URL 路径权限
- 使用过滤注解
- 动态权限
给用户设置角色和权限
在SpringSecurity中角色和权限并没有分开,而是都统一的封装到了一个 List中。其中通过ROLE_来区分该权限是角色还是权限。
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
System.out