SpringBoot整合SpringSecurity(入門級)

mmythos發表於2017-12-30

目錄

目錄建立於2017-12-18


Springboot整合SpringSecurity

Demo

快速上手-初步入門:

建立單使用者單角色的安全控制

  • 新增依賴
   <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
      @Autowired
      private ReaderRepository readerRepository;
      @Override
      protected void configure(HttpSecurity http) throws Exception {
        http
          .authorizeRequests()
            .antMatchers("/").access("hasRole(`READER`)")//要求登陸者進入根目錄必須具有 READER 的角色
            .antMatchers("/**").permitAll()//其他頁面開放了許可權
          .and()
          .formLogin()
            .loginPage("/login")//登入表單的路徑
            .failureUrl("/login?error=true");
      }
      @Override
      protected void configure(
                  AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(new UserDetailsService() {//定義自定義的UserDetailService
            @Override
            public UserDetails loadUserByUsername(String username)
                throws UsernameNotFoundException {
              UserDetails userDetails = readerRepository.findOne(username);
              if (userDetails != null) {
                return userDetails;
              }
              throw new UsernameNotFoundException("User `" + username + "` not found.");
            }
          });
      }
    }

Repository類

   public interface ReaderRepository extends JpaRepository<Reader, String> {}
    //登入實體類
    @Entity
    public class Reader implements UserDetails {
      private static final long serialVersionUID = 1L;
      @Id
      private String username;
      private String fullname;
      private String password;
      //省略setget
      //授予READER許可權
      @Override
      public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.asList(new SimpleGrantedAuthority("ROLE_READER"));
      }
      //不過期
      @Override
      public boolean isAccountNonExpired() { return true;}
      //不加鎖
      @Override
      public boolean isAccountNonLocked() {return true;}
      //不禁用
      @Override
      public boolean isCredentialsNonExpired() {return true;}
      //可用
      @Override
      public boolean isEnabled() { return true;}
    }

多使用者多角色的實現思路

  • 使用多個實體類(實現了UserDetails介面),一個許可權類,再一個多對多連線,就得到了多使用者,多許可權的控制
  • 在頁面上加上角色的判斷來控制資料顯示,業務操作等功能

  • 根據書上案例程式碼,可以得出結論,使用者表,角色表,使用者角色關聯表,使用者表是可以多張的,角色公用一張即可,然後關聯表也對應的多張,就能實現具體的業務需求

    • 例如:一個網上線上閱讀書城,作家和讀者以及編輯,網站後臺管理員等角色的不同許可權對應的頁面甚至頁面上細分的各個模組
  • Author Admin Reader 三個類
    繼承了UserDetails介面的實體類的配置

    //配置多對多的關係,使用者和角色(許可權)之間的關係,是通用的改下屬性名即可
    @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
    private List<AllRoles> roles ;
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
        List<AllRoles> roles = this.getRoles();
        for(AllRoles role:roles){
            auths.add(new SimpleGrantedAuthority(role.getRole_name()));
        }
        return auths;
    }
    // 登入的使用者名稱,如果屬性不是叫username,就要重寫,返回應該的使用者名稱屬性就是了
    @Override
    public String getUsername() { return this.reader_name;}
    @Override
    public boolean isAccountNonExpired() {return true;}
    @Override
    public boolean isAccountNonLocked() {return true;}
    @Override
    public boolean isCredentialsNonExpired() {return true;}
    @Override
    public boolean isEnabled() {return true;}
每個身份都使用一個登入實體類
  • 然後使用不同的dao層查詢,顯然的實體類登入查詢的效率及其低且不易擴充套件
  • 設定好spirng.jpa.hibernate.ddl-auto=update
  • 第一次執行還會有沒有實體對應的表這樣的提示,說明了他正在根據多對多對映建立實體表,也體現了這個多種使用者模式下需要實體等量的連線表
  • 所以這個是要查詢多張表了
    • (除非UserDetailService介面的loadUserByUsername能收到表類別的引數)
    • 也可以考慮使用一個字串,然後用特殊字元把型別放進去,然後正則取出來
    • 登入頁面就需要自定義一個函式進行拼接(或者使用校驗來拼接?)
另一種思路:
  • 使用一個登入使用者表(序列id,使用者名稱,密碼,使用者編碼(對應多張表))
  • 角色表(序列id,使用者編碼,角色)
    這樣的話擴充套件就只要加表,使用同一個主鍵生成策略就可以了

  • 思考:

    • 其實這個安全框架使用的是角色控制,而不是許可權控制,目前的瞭解,達不到Oracle那樣的許可權加角色控制

實現細節

關於註解的幾種使用方式

@Secured

  • 這是基於Spring特定的註解

@RolesAllowed

  • JSR-250的@RolesAllowed Java標準定義的註解,與上面的註解是差不多的
  • 但是都是有侷限性,只能判斷請求是否有許可權,不能進行更多的自定義判斷

SpringSecurity3.0 開始提供了 SpEL表示式

需要先配置這個配置類,後面的註解才會生效

    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{}
  • @PreAuthorize 方法呼叫前,基於表示式的計算結果來限制方法的訪問
  • @PostAuthorize 允許方法呼叫,如果表示式是false 丟擲安全異常
  • @PostFilter 允許方法呼叫,按照表示式來過濾方法的結果
  • @PreFilter 允許方法呼叫,必須進入方法前過濾輸入值

  • 方法呼叫前驗證許可權,示例:

    • @PreAuthorize("hasRole(`ROLE_ADMIN`)") 只允許該許可權的使用者訪問
    • 方法入參user,限定讀者使用者的text長度小於140,或者是作家使用者無限制
    • @PreAuthorize("(hasRole(`ROLE_READER`) and #user.text.length()<=140 ) or hasRole(`ROLE_AUTHOR`)")
  • 方法呼叫之後驗證許可權,示例;
    • @PostAuthorize("returnObject.spitter.username == principal.username")
    • public Spittle getSpittleById(long id){}
    • 保護方法,只有當返回的物件就是當前登入的使用者時,才返回,不然丟擲安全異常
      以上是保護方法的呼叫,但是有時候保護的是資料的輸入輸出:
  • 過濾方法的輸入輸出
    • 事後對方法的返回值進行過濾
      • @PreAuthorize("hasAnyRole({`ROLE_ADMIN`,`ROLE_USER`})")
      • @PostFilter("hasRole(`ROLE_ADMIN`) || filterObject.user.username == principal.name")
      • public List<User> getUsers(){}
      • 該示例就是限制瞭如果是管理員可以獲取到所有資料,普通使用者只能看到自己
      • 但是這種實現是不好的,只是一個例子,只獲取自己,過載方法加個id引數就好了,上面的實現,把資料全拿出來再判斷,效能上。。。
    • 事先對方法的引數進行過濾
      • @PreAuthorize("hasAnyRole({`ROLE_ADMIN`,`ROLE_USER`})")
      • @PreFilter("hasRole(`ROLE_ADMIN`) || targetObject.user.username == principal.name")
      • public void deleteUsers(){List<User> users}
      • 示例實現了傳入一個集合,要刪除的使用者,但是當前使用者只能刪除自己,管理員才能刪除集合裡所有的使用者
    • 定義許可計算器
      • @PreFilter("hasPermission(targetObject,`delete`)") 使用者是否有許可權刪除目標物件?
      • 使用了自定義的計算器類來實現這個判斷,表示式簡潔,但是自定義類不好寫
      • 實現PermissionEvaluator介面,新建hasPermission方法,功能就是判斷是否有許可權,其實就是對比目標物件是不是當前使用者
      • 建立好類後,過載GlobalMethodSecurityConfiguration配置類的createExpressionHalder方法,註冊進去
      • DefaultMethodSecurityExperssionHandler ex = new De...();
      • ex.setPermissionEvaluator(new 自定義類);
      • return ex;

保護方法應用

  • @Secured 註解限制方法呼叫


相關文章