零、系列
歡迎來嫖從零開始SpringCloud Alibaba電商系列:
- 從零開始SpringCloud Alibaba電商系統(一)——Alibaba與Nacos服務註冊與發現
- 從零開始SpringCloud Alibaba電商系統(二)——Nacos配置中心
- 從零開始SpringCloud Alibaba電商系統(三)——Sentinel流量防衛兵介紹、流量控制demo
- 從零開始SpringCloud Alibaba電商系統(四)——Sentinel的fallback和blockHandler
- 從零開始SpringCloud Alibaba電商系統(五)——Feign Demo,Sentinel+Feign實現多節點間熔斷/服務降級
- 從零開始SpringCloud Alibaba電商系統(六)——Sentinel規則持久化到Nacos配置中心
- 從零開始SpringCloud Alibaba電商系統(七)——Spring Security實現登錄認證、權限控制
- 從零開始SpringCloud Alibaba電商系統(八)——用一個好看的Swagger接口文檔
- 從零開始SpringCloud Alibaba電商系統(九)——基於Spring Security OAuth2實現SSO-認證服務器(非JWT)
一、 概述
前文我們考慮了OAuth2的鑑權方式,但是作爲B2C電商系統來說,它的功能有些超出需求範圍。
OAuth2的目的是認證中心要不要給服務提供者授權。
而我們當前權限設計所需要的是:
能讓多子系統共享用戶狀態,擁有相同的鑑權模式。
基於以上考慮,本文將嘗試採用redis做session保存用戶信息,在各個子服務系統複用相同的認證鑑權,並依然借用spring security的權限控制功能,基本的系統角色有如下三種:
基本登錄流程如下:
當然,我們還需要考慮角色對資源的權限,通過spring security來實現接口的訪問限制,通過用戶-角色-資源等庫表來實現資源粒度,表結構如下:
基本瞭解了概念,接下來我們就從spring security配置redis session開始,實現一個可用的權限認證功能。
二、如何整合Redis做Session
有一些方式可以選擇:
- spring security原本是使用concurrentHashMap來做session的,我們只需要將session的相關實現類替換成redis爲數據存儲結構 。SessionRegistryImpl是我們需要覆蓋重寫的類,然後再spring security配置時將我們的實現類註冊進去。
- 直接使用Spring-Session-Redis,樂樂十分推薦第二種方式,畢竟都是自己做的框架,用起來就是兩個字——絲滑。
三、準備環境
Redis Server 一臺。
本地代碼 一堆:樂樂這裏用的是父子項目。
mall是最外層父模塊。
common爲所有子系統的依賴模塊,認證邏輯會寫在這裏,這個模塊不會作爲微服務中的一個來單獨部署,下面只是爲了方便演示。
user爲一個子系統模塊,依賴common。
暫時就用一個common,一個user來演示兩個客戶端。
四、實現流程
數據庫表放在了flywaydb文件中,會隨項目啓動自行創建到數據庫中。其他相關的配置文件不再贅述,有需要的同學可以直接去底部拿demo。
common模塊
主要的認證邏輯集中在common模塊,然後其他子系統都會引用common的代碼。
- 使用上述表結構,實現我們的自定義UserDetails,這裏的mapper由逆向工程實現。
具體的sql和代碼放在最下方demo中。 - 爲父子模塊配置依賴,如果在自己項目中,已經有security的話可以只引入spring-session-redis。
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
- 配置文件,application.properties的內容主要是redis相關(在spring security配置健全的前提下),其中redis.xxx的配置爲筆者自定義。
redis.host=localhost
redis.port=6379
session.redis.timeout=60000
spring.session.store-type=redis
server.servlet.session.timeout=120
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session
- 配置Redis的相關Bean,需要一個Java客戶端,這裏使用Lettuce。
這裏使用的redis.XXX就是前一步配置文件中配置的。
@Configuration
public class RedisConfig {
@Value("${redis.host}")
private String redisServer;
@Value("${redis.port}")
private Integer redisPort;
@Value("${redis.password}")
private String redisPassword;
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisServer, redisPort);
redisStandaloneConfiguration.setPassword(redisPassword);
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}
}
-
使用RBAC數據庫結構實現UserDetails。
筆者這裏使用了easycode+mybatisplus逆向生成了這些表的代碼,不想折騰的同學可以直達底部。
唯一個人寫的就是MPermissionDao.selectPermissionByUserId方法,用來查詢該用戶所擁有的所有資源權限。
-
這裏只是在user表插入了一條用戶名密碼都爲root的數據,用來進行redis session的流程,當然密碼需要加密,筆者在單元測試方法寫了一些插入數據時加密密碼的測試方法,可以參考。
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperTest {
@Autowired
private MUserDao mUserDao;
// Bean來自WebSecurityConfiguration
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Test
public void testInsert(){
MUser user = MUser.builder().username("root").password(bCryptPasswordEncoder.encode("root")).build();
mUserDao.insert(user);
}
}
到此爲止,common模塊已經可以測試使用了,單模塊測試,我們跑一下。
7. 啓動common的application,root/root登錄。
查看我們的redis庫是否有了session數據,成功!內容我們就不分析了,名字敘述的很清晰。
User模塊
-
新建user子模塊之前,需要對common模塊做一個小小的改動,那就是增加spring.factories,否則啓動user模塊時,common模塊的配置類是不會被加載的。
-
新建user模塊,直接新建一個springboot項目即可,將parent改爲mall。然後依賴只需要添加common,這個字數比較少直接貼上來。
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>mall</artifactId>
<groupId>org.lele</groupId>
<version>0.1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
<artifactId>user</artifactId>
<dependencies>
<dependency>
<groupId>org.lele</groupId>
<artifactId>common</artifactId>
<version>0.1</version>
</dependency>
</dependencies>
</project>
- 按照common的配置,配置一份user的,在本節demo中,需要修改的有服務名、服務端口、flywaydb的名稱(不用也可以不改)。
不想使用nacos配置中心的同學可以直接看下面貼的這一坨
spring.application.name=user
server.port=8083
spring.cloud.nacos.discovery.server-addr=localhost:8848
management.endpoints.web.exposure.include=*
#spring.cloud.sentinel.enabled=false
#spring.cloud.sentinel.transport.port=8121
#spring.cloud.sentinel.transport.dashboard=localhost:8080
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.url= jdbc:mysql://localhost:3306/mall_user?useSSL=false&useUnicode=true&characterEncoding=UTF-8
spring.datasource.driver-class-name= com.mysql.jdbc.Driver
spring.datasource.username= root
spring.datasource.password= root
spring.datasource.druid.initial-size= 5
spring.datasource.druid.max-active=5
spring.datasource.druid.max-wait=10000
# flyway enable
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=true
spring.flyway.locations=classpath:db/migration
spring.flyway.table=common_version
spring.flyway.baseline-version=0
spring.flyway.encoding=UTF-8
spring.flyway.validate-on-migrate=false
spring.flyway.placeholder-prefix=##(
spring.flyway.placeholder-suffix=)
#mybatis-plus
mybatis-plus.mapper-locations= classpath:/mapper/**.xml
#redis
redis.host=localhost
redis.port=6379
redis.password=redisPassword
session.redis.timeout=60000
spring.session.store-type=redis
server.servlet.session.timeout=120
spring.session.redis.flush-mode=on-save
spring.session.redis.namespace=spring:session
- user模塊配置完畢,啓動application。
訪問 localhost:8083
可以不用登陸直接訪問到user模塊的服務,這是因爲之前common也就是8082服務已經將seesion註冊到redis中了。當然,換一個(或重啓)瀏覽器肯定還是要重新登錄的。
五、demo
至此,spring security配置完畢,權限系統也可以直接使用了,數據可以自己添加,最好是配合一個後端管理系統來管理資源權限,當然這個樂樂(筆者)之後也會慢慢做出來的。
https://github.com/flyChineseBoy/lel-mall/tree/master/mall10