1. 創建項目
使用idea
中的spring
初始化工具引入springboot
和springsecruity
初始化項目
最終pom.xml
如下
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo-spring-secruity</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo-spring-secruity</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<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>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
創建一個測試控制器
package com.example.demospringsecruity.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author john
* @date 2020/1/6 - 9:47
*/
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello spring secruity";
}
}
運行項目
訪問<http://localhost:8081/hello>
,會被跳轉到<http://localhost:8081/login>
輸入用戶名user
和剛才運行項目時輸出的密碼,登錄成功,可以獲取到剛才預期的響應
2. 自定義登錄方式
package com.example.demospringsecruity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author john
* @date 2020/1/6 - 10:07
*/
@Configuration
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表單登錄
// http.formLogin()
// 基本登錄
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
}
當瀏覽器發送一個請求后,首先會經過第一個過濾器,UsernamePassowordAuthenticationFilter
,這個過濾器中,它會判斷你的請求是否帶username
和password
這兩個參數,如果帶了就進行攔截驗證,如果沒有就進入到第二個過濾器BasicAuthenticationFilter
,這個過濾器會判斷請求頭中是否含有需要驗證的信息,如果沒有進入下一個攔截器,最后到ExceptionTranslationFilter
攔截器,它會捕獲FilterSecurityInterceptor
拋出的異常,而FilterSecurityInterceptor
會判斷這個請求是否校驗通過,權限是否通過,以上就是Spring Security
框架的一個基本認證流程,FilterSecurityInterceptor
是這個框架的最后一個攔截器,所有的請求都必須通過該攔截器的校驗。
3. 自定義用戶認證邏輯
1. 處理用戶信息獲取邏輯 UserDetailsService
2. 處理用戶校驗邏輯 UserDetails
3. 處理密碼加密解密 PasswordEncoder
UserDetailsService
用戶信息獲取邏輯在springsecruity
中是被封裝在UserDetailService
接口中
package org.springframework.security.core.userdetails;
public interface UserDetailsService {
UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException;
}
UserDetails
public interface UserDetails extends Serializable {
Collection<? extends GrantedAuthority> getAuthorities();//權限的集合
String getPassword();//密碼
String getUsername();//用戶名
boolean isAccountNonExpired();//賬戶是否沒過期 true-->沒過期
boolean isAccountNonLocked();//賬戶是否沒鎖定 true-->沒被鎖定
boolean isCredentialsNonExpired();//憑證是否沒過期 true-->沒過期
boolean isEnabled();//賬戶是否可用
}
下面開始編寫具體代碼
MyUserDetailsService處理用戶信息獲取邏輯
package com.example.demospringsecruity.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
* @author john
* @date 2020/1/6 - 10:32
*/
@Component
@Slf4j
public class MyUserDetailsService implements UserDetailsService {
//這里可以注入mapper或者repository的dao對象來實現數據校驗邏輯操作
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
log.info("用戶名:" + username);
//這里密碼應該從數據庫中取出,暫時先使用加密生成
String password = passwordEncoder.encode("123456");
//User類是Spring內置的一個類,實現了UserDetails接口,而這個接口是UserDetailSerice的子接口
return new User(username,
password,
true, // 賬戶是否可用
true, // 賬戶是否過期
true, // 密碼是否過期
true, //賬戶是否被鎖定
AuthorityUtils.commaSeparatedStringToAuthorityList("admin") //授權集合
);
}
}
PasswordConfig處理密碼加密解密
注入PasswordEncoder
package com.example.demospringsecruity.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* @author john
* @date 2020/1/6 - 12:51
*/
//說明這個一個配置類,類似spring中的xml文件
@Configuration
public class PasswordConfig {
//手動將PasswordEncoder注入到ioc容器中
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
測試在輸入用戶user
密碼123456
后成功獲取數據
上面的演示我使用了spring-security
自己的UserDetails
實現類User
(org.springframework.security.core.userdetails.User
),但實際開發中我們不可能這么用,因為根據實際的業務,往往會希望用戶信息表里會包含更多的數據,比如說電話,郵箱,公司職位等信息,那我們該如何去處理呢?
其實非常簡單,我們只要將我們自己的User
表繼承spring-security
的User
類(org.springframework.security.core.userdetails.User
),再在MyUserDetailsService
里只要返回我們自己的user
即可.