SpringBoot2.1版本的我的應用開發框架 - 使用SpringSecurity管理咱們的訪問權限1

本篇做爲SpringBoot2.1版本的我的開發框架 子章節,請先閱讀SpringBoot2.1版本的我的開發框架再次閱讀本篇文章html

後端項目地址:SpringBoot2.1版本的我的應用開發框架前端

前端項目地址:ywh-vue-adminvue

參考:java

SpringSecurity和jjwt簡介

SpringSecurity 是專門針對基於Spring項目的安全框架,充分利用了AOP和Filter來實現安全功能。它提供全面的安全性解決方案,同時在 Web 請求級和方法調用級處理身份確認和受權。他提供了強大的企業安全服務,如:認證受權機制、Web資源訪問控制、業務方法調用訪問控制、領域對象訪問控制Access Control List(ACL)、單點登陸(SSO),Web 應用的安全性包括用戶認證(Authentication)和用戶受權(Authorization)等等。git

核心功能:認證(你是誰)、受權(你能幹什麼)、攻擊防禦(防止僞造身份)。github

而jwt全稱叫作JSON WEB TOKEN,網上對於jwt的概念仍是不少的,而個人理解就是把用戶的信息封裝成一串加密的字符串,而jwt的格式例如:A.B.C,有三個部分組成,中間有 . 相隔,每一節都是base 64編碼的。web

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY
複製代碼

A的部分叫作Header,必須指定用於簽署JWT的算法。 B的部分叫作body,本節包含了JWT編碼的全部聲明,例如能夠把咱們用戶的信息編進去。 最後一部分是signature,它的組成是前兩部分,它經過在頭文件中指定的算法經過header和body的組合來計算。 更詳細的介紹,能夠參考:jjwt的github文檔算法

簡單配置Spring Security

建立

配置Security以前咱們先對ywh-starter-security模塊進行劃分,如下包的命名和功能徹底能夠本身規定。spring

  • controller包:登錄接口以及其餘系統接口
  • dao包:查詢系統用戶的接口
  • entity包:實體類
  • fiter包:攔截器
  • service和impl包:service層和實現類
  • utils包:工具包
  • config包:配置類
  • resources/security-mybatis-mappers:系統類的mapper.xml文件

修改

建立完之後,咱們須要對項目進行如下小的修改,讓項目識別咱們security下的東西數據庫

  • 在core模塊下啓動類上的@MapperScan註解修改以下
@MapperScan(basePackages = "com.ywh.**.dao")
複製代碼
  • 在core模塊中的application-dev.yml文件中設置mapper-locations屬性,多路徑以逗號相隔
mapper-locations: classpath*:/mybatis-mappers/*,classpath*:/security-mybatis-mappers/*
複製代碼
  • 在Security模塊的pom.xml中引入springsecurity的依賴以下:
<!--security的依賴-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
	<version>2.1.2.RELEASE</version>
</dependency>
複製代碼

開始

當咱們引入依賴後,什麼也不作咱們就可使用到Security給咱們帶來的登錄功能,啓動項目後訪問咱們的任意接口會被跳轉到login頁面,帳戶和密碼Security也給咱們默認了。

springSecurity登陸界面

咱們在控制檯中能夠看到如下內容:Using generated security password: 5dd9438d-1c10-44c2-bc45-6864ba4308ae

控制檯信息

這個是Security給咱們自動生成的登錄密碼,帳戶是什麼呢?咱們進入到圖片中標紅的類UserDetailsServiceAutoConfiguration能夠看到如下內容:

@Lazy
public InMemoryUserDetailsManager inMemoryUserDetailsManager(SecurityProperties properties, ObjectProvider<PasswordEncoder> passwordEncoder) {
    User user = properties.getUser();
    List<String> roles = user.getRoles();
    return new InMemoryUserDetailsManager(new UserDetails[]{org.springframework.security.core.userdetails.User.withUsername(user.getName()).password(this.getOrDeducePassword(user, (PasswordEncoder)passwordEncoder.getIfAvailable())).roles(StringUtils.toStringArray(roles)).build()});
}

private String getOrDeducePassword(User user, PasswordEncoder encoder) {
    String password = user.getPassword();
    if (user.isPasswordGenerated()) {
    	// 這個就是咱們在控制檯中看到的那句話的代碼,是經過user.getPassword()來獲取密碼的
        logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
    }

    return encoder == null && !PASSWORD_ALGORITHM_PATTERN.matcher(password).matches() ? "{noop}" + password : password;
}
複製代碼

很明顯Security是經過User這個類來進行管理默認的用戶信息的,在內部類User中替咱們設置了默認的帳戶「user」,密碼是一個隨機的UUID,咱們經過帳戶和密碼登錄之後就能夠訪問個人接口了。

public static class User {
        private String name = "user";
        private String password = UUID.randomUUID().toString();
        private List<String> roles = new ArrayList();
        private boolean passwordGenerated = true;

        public User() {
        }

        。。。此處省略瞭如下代碼
    }
複製代碼

若是咱們想要本身設置登錄的帳號和密碼,能夠在application.yml文件中添加如下內容就能夠了。

spring:
 security:
 user:
 name: admin
 password: admin
複製代碼

定製Spring Security配置

參考:

在上面其實什麼也沒有動,只是最簡單的引入了依賴而已,啓動項目之後Security自動爲咱們配置了認證安全機制,很顯然這對於咱們來講是不夠的,因此咱們須要對security進行本身定製化配置。

除了在咱們的yml文件中配置默認的登錄帳戶和密碼之外,咱們還能夠經過繼承WebSecurityConfigurerAdapter 類來實現,這是核心類之一。

在這個類中有三個configure方法

方法 描述
configure(AuthenticationManagerBuilder auth) 用戶信息的配置
configure(WebSecurity web) 配置Spring Security的Filter鏈
configure(HttpSecurity http) 配置如何經過攔截器保護咱們的請求,哪些能經過哪些不能經過

咱們須要經過重寫configure(AuthenticationManagerBuilder auth)來配置咱們的用戶,如下方式在security5.0之前這麼寫是沒有問題的,security默認使用的是NoOpPasswordEncoder編碼,就是沒有加密的方式,純文本編碼,你輸入root那麼密碼就是明文顯示的root了,如今NoOpPasswordEncoder已經被廢棄了,由於不安全。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
	@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("root")
                .password("root")
                .roles("user");
    }
}
複製代碼

按照上面方式配置用戶的話,控制檯會報錯**java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"**大概意思就是說咱們沒有設置加密的方式。

而NoOpPasswordEncoder已經廢棄了,因此咱們來實現一個相似的內部類作測試使用便可,僅僅作測試時使用,從新啓動項目時咱們會在控制檯看見依舊輸出password,可是已經不生效了,使用本身設置的root登錄便可,若是想設置多個用戶的話,經過.and()鏈接後再次設置就行了。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
	@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("root")
                .password("root")
                .roles("user")
                .and()
                .passwordEncoder(CharEncoder.getINSTANCE());
    }

	//如下方式就是設計模式中的單例模式的餓漢式,正好練習一下。
    public static class CharEncoder implements PasswordEncoder {

        public  static CharEncoder INSTANCE = new CharEncoder();

        public static CharEncoder getINSTANCE(){
            return INSTANCE;
        }

        @Override
        public String encode(CharSequence rawPassword) {
            return rawPassword.toString();
        }

        @Override
        public boolean matches(CharSequence rawPassword, String encodedPassword) {
            return rawPassword.toString().equals(encodedPassword);
        }
    }
}
複製代碼

以上配置好之後security對咱們全部的請求都進行了攔截,咱們靜態文件和其餘不須要認證的都被攔截顯然不是咱們所但願的,因此咱們要對某些請求放開,這個就須要咱們重寫configure(HttpSecurity http)這個方法了,首先咱們看一下默認的方法。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
	protected void configure(HttpSecurity http) throws Exception {
		((HttpSecurity)((HttpSecurity)((AuthorizedUrl)http.authorizeRequests()
		.anyRequest()).authenticated()
		.and())
		.formLogin()
		.and())
		.httpBasic();
	}
}
複製代碼
  • anyRequest:對於任何的請求都進行攔截認證
  • permitAll() 方法會對指定的路徑放行
  • authenticated() 方法會對指定的路徑進行攔截認證,若是未登錄則跳轉到登錄頁面

而咱們想要對某些接口對外開放,好比ExampleController下的securityTest1接口放行和對是post類型的securityTest2放行,其餘接口進行攔截。

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfigurer extends WebSecurityConfigurerAdapter {
/** * 配置如何經過攔截器保護咱們的請求,哪些能經過哪些不能經過,容許對特定的http請求基於安全考慮進行配置 * @param httpSecurity http * @throws Exception 異常 */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                .authorizeRequests()
                .antMatchers("/example/securityTest1").permitAll()
                .antMatchers(HttpMethod.POST,"/example/securityTest2").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin();
    }
}
複製代碼

當咱們定義了一個securityTest2接口是GET請求的話,就不會放行,只有是POST類型的securityTest2纔會被放行經過,這就好像是你拿着別人的身份證去辦銀行卡,銀行固然不可能給你放行。

獲取登錄的用戶

當咱們登錄之後,咱們想要獲取一下登錄用戶的信息,在Spring Security中,用戶信息保存在SecurityContextHolder中,咱們能夠經過如下代碼獲取用戶的信息。

@GetMapping("securityTest2")
public Result securityTest2(){
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if(principal instanceof UserDetails){
        String name =  ((UserDetails) principal).getUsername();
        return Result.successJson("登錄的用戶是:" + name);
    }else {
        String name = principal.toString();
        return Result.successJson("登錄的用戶是:" + name);
    }
}
複製代碼

在這裏插入圖片描述

在咱們上面的配置事後咱們對security初步的有了一個認識,可是咱們知道對於網站來講,用戶的信息都是存在數據庫中的,不可能向咱們這樣寫死在代碼中的,實現從數據庫中查詢實現登錄我將在下一篇筆記中記錄,一篇文章太多了,看不進去。

相關文章
相關標籤/搜索