SpringSecurity学习记录

1、简介
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。
2、与Shiro区别:
Shiro是一个强大而灵活的开源安全框架,能够非常清晰的处理认证、授权、管理会话以及密码加
密。如下是它所具有的特点:
①易于理解的 Java Security API;
②简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory
等);
③对角色的简单的鉴权(访问控制),支持细粒度的鉴权;
④支持一级缓存,以提升应用程序的性能;
⑤内置的基于 POJO 企业会话管理,适用于 Web 以及非 Web 的环境;
⑥异构客户端会话访问;
⑦非常简单的加密 API;
⑧不跟任何的框架或者容器捆绑,可以独立运行。
Spring Security:
除了不能脱离Spring,shiro功能Security都具备;并且Spring Security对Oauth openid也有支持,shiro需要手动实现,Spring Security权限颗粒度也更高

3、应用场景
①用户登录,基于web开发的项目登录功能
②用户授权
③单一登录:一个账号同一时间只能在一个地方进行登录,如果在其他登录二次登录,则剔除前面的登录操作
④集成CAS,做单点登录
⑤集成Oauth2,做授权登录(第三方登录等),也可以实现cas

4.Spring Security认证基本原理
①使用方式:引入依赖

   
   
      org.springframework.boot 
      spring-boot-starter-security 
  

②原理:
Spring Security功能的实现主要是由一系列过滤器相互配合完
成。也称之为过滤器链
https://www.processon.com/diagraming/60889b756376896ee106ae10

Spring Security执行流程图.png

③认证方式:
⑴HttpBasic认证:Spring Security实现登录验证最简单的一种方式,Security 4.X版本,无需任何配置,启动项目访问则会弹出默认的httpbasic认证,在spring security 5.x默认的验证模式已经是表单模式。HttpBasic模式要求传输的用户名密码使用Base64模式进行加密。
⑵formLogin登录认证模式:spring boot2.0以上版本(依赖Security 5.X版本)默认会生成一个登录页面.同样,我们也可以选择自定义登录页面

④安全构建器 HttpSecurity 和 WebSecurity 的区别:
⑴、WebSecurity 不仅通过 HttpSecurity 定义某些请求的安全控制,也通过其他方式定义其他某些请求可以忽略安全控制;
⑵、HttpSecurity 仅用于定义需要安全控制的请求(当然 HttpSecurity 也可以指定某些请求不需要安全控制);
⑶、可以认为 HttpSecurity 是 WebSecurity 的一部分, WebSecurity 是包含 HttpSecurity 的更大的一个概念;
⑷、构建目标不同:WebSecurity 构建目标是整个 Spring Security 安全过滤器 FilterChainProxy`;HttpSecurity 的构建目标仅仅是 FilterChainProxy 中的一个 SecurityFilterChain 。

⑤表单登录:
⑴UsernamePasswordAuthenticationFilter过滤器源码分析

public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");

表单中的input的name值是username和password, 并且表单提交的路径为 /login , 表单提交方式method为 post , 这些可以修改为自定义的值.即在自定义SecurityConfig配置类中:

  http.formLogin()//开启表单认证
            .loginPage("/toLoginPage")//自定义登录页面
            .loginProcessingUrl("/login")//自定义表单提交路径
            .usernameParameter("username")
            .passwordParameter("password")//自定义input值
            .successForwardUrl("/")//指定登录成功后跳转的路径
            .and().authorizeRequests().antMatchers("/toLoginPage").permitAll()//放行登陆页面
        .anyRequest().authenticated();
    /**
     * 关闭csrf防护
     */
    http.csrf().disable();
    /**
     * 加载同源域名下iframe页面
     */
    http.headers().frameOptions().sameOrigin();

⑵基于数据库实现认证功能:
1、实现security的一个UserDetailsService接口, 重写这个接口里面
loadUserByUsername方法:

  /**
 * 根据username查询用户实体
 * @param username
 * @return
 * @throws UsernameNotFoundException
 */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userService.findByUsername(username);
    if(user==null){
        throw new UsernameNotFoundException(username+"用户没有找到");
    }
    //声明权限集合,因为构造方法中不能传null值
    Collection authorities = new ArrayList<>();

    UserDetails userDetails=new org.springframework.security.core.userdetails.User(user.getUsername()
    ,"{bcrypt}"+user.getPassword() //{noop}表示不加密 bcrypt表示bcrypt算法加密
    ,true //用户是否启用 true 代表启用
    ,true // 用户是否过期 true 代表未过期
    ,true // 用户凭据是否过期 true 代表未过期
    ,true // 用户是否锁定 true 代表未锁定
    ,authorities);
    return userDetails;
}

2、在SecurityConfig配置类中指定自定义用户认证

  /**
 * 身份验证管理器
 * @param auth
 * @throws Exception
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //使用自定义用户认证
    auth.userDetailsService(myUserDetailsService);
}

⑶密码加密认证:
在基于数据库完成用户登录的过程中,我们所是使用的密码是明文的,规则是通过对密码明文添加{noop} 前缀。
Spring Security 中 PasswordEncoder 就是我们对密码进行编码的工具接口。该接口只有两个功能:一个是匹配验证。另一个是密码编码

  public interface PasswordEncoder {
    String encode(CharSequence var1);

    boolean matches(CharSequence var1, String var2);

    default boolean upgradeEncoding(String encodedPassword) {
    return false;
    }
  }

密码工厂PasswordEncoderFactories:

  public class PasswordEncoderFactories {
      public static PasswordEncoder createDelegatingPasswordEncoder() {
          String encodingId = "bcrypt";
          Map encoders = new HashMap();
          encoders.put(encodingId, new BCryptPasswordEncoder());
          encoders.put("ldap", new LdapShaPasswordEncoder());
          encoders.put("MD4", new Md4PasswordEncoder());
          encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
          encoders.put("noop", NoOpPasswordEncoder.getInstance());
          encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
          encoders.put("scrypt", new SCryptPasswordEncoder());
          encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
          encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
          encoders.put("sha256", new StandardPasswordEncoder());
          encoders.put("argon2", new Argon2PasswordEncoder());
      return new DelegatingPasswordEncoder(encodingId, encoders);
      }

      private PasswordEncoderFactories() {
      }
  }

⑦退出登录:LogoutSuccessHandler处理器

  //设置退出url 
  //自定义退出处理
  and().logout().logoutUrl("/logout").logoutSuccessHandler(myAuthenticationService)

⑧图形码验证:
spring security添加验证码大致可以分为三个步骤:
⑴、根据随机数生成验证码图片;
⑵、将验证码图片显示到登录页面;
⑶、认证流程中加入验证码校验。
Spring Security的认证校验是由UsernamePasswordAuthenticationFilter过滤器完成的,所以验证码校验逻辑应该在这个过滤器之前:
自定义验证码过滤器ValidateCodeFilter,需要继承OncePerRequestFilter确保在一次请求只通过一次filter,而不 需要重复执行
⑨session管理:
⑴、会话超时:
设置session管理和失效后跳转地址

  http.sessionManagement() //设置session管理 
  .invalidSessionUrl("/toLoginPage")// session无效后跳转的路径, 默认是登录页面

⑵、并发控制:SessionManagementFilter
修改超时时间:

  #session设置 #配置session超时时间 
  server.servlet.session.timeout=600

设置最大会话数量:

  http.sessionManagement()  //设置session管理        
  .invalidSessionUrl("/toLoginPage") // session无效后跳转的路径, 默 认是登录页面 
  .maximumSessions(1)//设置session最大会话数量 ,1同一时间只能有一个 用户登录 
  .expiredUrl("/toLoginPage");//设置session过期后跳转路径

阻止用户第二次登录:sessionManagement也可以配置maxSessionsPreventsLogin:boolean值,当达到maximumSessions设置的最大会话个数时阻止登录。

  http.sessionManagement()//设置session管理 
  .invalidSessionUrl("/toLoginPage")// session无效后跳转的路径, 默 认是登录页面 
  .maximumSessions(1)//设置session最大会话数量 ,1同一时间只能有一个 用户登录 
  .maxSessionsPreventsLogin(true)//当达到最大会话个数时阻止登录。       
  .expiredUrl("/toLoginPage");//设置session过期后跳转路径

5、SpringSecurity授权
①内置表达式:
Spring Security 使用Spring EL来支持,主要用于Web访问和方法安全上, 可以通过表达式来判断是否具有访问权限. 下面是Spring Security常用的内置表达式.ExpressionUrlAuthorizationConfigurer定义了所有的表达式

  表达式           说明
  permitAll        指定任何人都允许访问。
  denyAll 指定任何人都不允许访问
  anonymous 指定匿名用户允许访问。
  rememberMe 指定已记住的用户允许访问。
  authenticated 指定任何经过身份验证的用户都允许访问,不包含anonymous
  fullyAuthenticated 指定由经过身份验证的用户允许访问,不包含anonymous和rememberMe
  hasRole(role) 指定需要特定的角色的用户允许访问, 会自动在角色前面插入'ROLE_'
  hasAnyRole([role1,role2]) 指定需要任意一个角色的用户允许访问, 会自动在角色前面插入'ROLE_'
  hasAuthority(authority) 指定需要特定的权限的用户允许访问
  hasAnyAuthority([authority,authority]) 指定需要任意一个权限的用户允许访问
  hasIpAddress(ip) 指定需要特定的IP地址可以访问

②url安全表达式:
⑴、设置url访问权限:

  // 设置/user/** 访问需要ADMIN角色 
http.authorizeRequests().antMatchers("/user/**").hasRole("ADMIN"); 
  // 设置/user/** 访问需要PRODUCT角色和IP地址为127.0.0.1 
  .hasAnyRole("PRODUCT,ADMIN")     
  .http.authorizeRequests().antMatchers("/product/**") .access("hasAnyRole('ADMIN,PRODUCT') and hasIpAddress('127.0.0.1')"); 
  // 设置自定义权限不足信息
  .http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

⑵、 MyAccessDeniedHandler自定义权限不足类,实现AccessDeniedHandler处理器
⑶、设置用户对应的角色权限

③在Web 安全表达式中引用自定义Bean授权:
⑴、定义自定义授权类,类似:

  /*** 自定义授权类 */ 
  @Component public class MyAuthorizationService { 
  /*** 检查用户是否有对应的访问权限 
  ** @param authentication 登录用户 
  * @param request 请求对象 
  * @return */ 
  public boolean check(Authentication authentication, HttpServletRequest request) {
    User user = (User) authentication.getPrincipal(); 
    // 获取用户所有权限 
    Collection authorities = user.getAuthorities(); 
    // 获取用户名 
    String username = user.getUsername(); 
    // 如果用户名为admin,则不需要认证 
    if (username.equalsIgnoreCase("admin")) { 
          return true; 
    } else { 
      // 循环用户的权限, 判断是否有ROLE_ADMIN权限, 有返回true 
      for (GrantedAuthority authority : authorities) { 
          String role = authority.getAuthority(); 
          if ("ROLE_ADMIN".equals(role)) { 
              return true; 
          } 
      }
     }
              return false;
     } 
    }

⑵、配置类

  //使用自定义Bean授权 
  http.authorizeRequests().antMatchers("/user/**")
  .access("@myAuthorizationService.check(authentication,request)");

⑶、携带路径变量

  //使用自定义Bean授权,并携带路径参数 
  http.authorizeRequests().antMatchers("/user/delete/{id}"). 
  access("@myAuthorizationService.check(authentication,request,#id)");

④Method安全表达式:
针对方法级别的访问控制比较复杂, spring security 提供了4种注解分别是:
@PreAuthorize :注解适合进入方法前的权限验证
@PostAuthorize :在方法执行后再进行权限验证,适合验证带有返回值的权
限, Spring EL 提供返回对象能够在表达式语言中获取到返回对象的 returnObject
@PreFilter : 可以用来对集合类型的参数进行过滤, 将不符合条件的元素剔除集合
@PostFilter : 可以用来对集合类型的返回值进行过滤, 将不符合条件的元素剔除集合

你可能感兴趣的:(SpringSecurity学习记录)