本篇做爲SpringBoot2.1版本的我的開發框架 子章節,請先閱讀SpringBoot2.1版本的我的開發框架再次閱讀本篇文章html
後端項目地址:SpringBoot2.1版本的我的應用開發框架前端
前端項目地址:ywh-vue-adminvue
參考:java
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文檔算法
配置Security以前咱們先對ywh-starter-security模塊進行劃分,如下包的命名和功能徹底能夠本身規定。spring
建立完之後,咱們須要對項目進行如下小的修改,讓項目識別咱們security下的東西數據庫
@MapperScan(basePackages = "com.ywh.**.dao")
複製代碼
mapper-locations: classpath*:/mybatis-mappers/*,classpath*:/security-mybatis-mappers/*
複製代碼
<!--security的依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
複製代碼
當咱們引入依賴後,什麼也不作咱們就可使用到Security給咱們帶來的登錄功能,啓動項目後訪問咱們的任意接口會被跳轉到login頁面,帳戶和密碼Security也給咱們默認了。
咱們在控制檯中能夠看到如下內容: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之httpSecurity使用實例,雖然這個是15年做者發佈的,可是仍是有參考價值的,新版本跟舊版本不同時還能夠加深咱們對新版本的認識。
在上面其實什麼也沒有動,只是最簡單的引入了依賴而已,啓動項目之後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();
}
}
複製代碼
而咱們想要對某些接口對外開放,好比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初步的有了一個認識,可是咱們知道對於網站來講,用戶的信息都是存在數據庫中的,不可能向咱們這樣寫死在代碼中的,實現從數據庫中查詢實現登錄我將在下一篇筆記中記錄,一篇文章太多了,看不進去。