Spring Cloud下實(shí)現(xiàn)用戶鑒權(quán)的方案
Java下常用的安全框架主要有Spring Security和shiro,都可提供非常強(qiáng)大的功能,但學(xué)習(xí)成本較高。在微服務(wù)下鑒權(quán)多多少少都會(huì)對(duì)服務(wù)有一定的入侵性。 為了降低依賴,減少入侵,讓鑒權(quán)功能相對(duì)應(yīng)用服務(wù)透明,我們采用網(wǎng)關(guān)攔截資源請(qǐng)求的方式進(jìn)行鑒權(quán)。
一、整體架構(gòu)
用戶鑒權(quán)模塊位于API GateWay服務(wù)中,所有的API資源請(qǐng)求都需要從此通過。
- 做身份認(rèn)證,通過則緩存用戶權(quán)限數(shù)據(jù),不通過返回401
- 做用戶鑒權(quán),比對(duì)當(dāng)前訪問資源(URI和Method)是否在已緩存的用戶權(quán)限數(shù)據(jù)中,在則轉(zhuǎn)發(fā)請(qǐng)求給對(duì)應(yīng)應(yīng)用服務(wù),不在則返回403
二、實(shí)現(xiàn)步驟
1. 用戶登陸
public LoginUser login(String userName, String password){ // 檢查密碼 User user = userService.checkUser(userName, password); LoginUser loginUser = LoginUser.builder() .userName(userName) .realName(user.getRealName()) .userToken(UUID.randomUUID().toString()) .loginTime(new Date()) .build(); // 保存session session.saveSession(loginUser); // 查詢權(quán)限 List<Permission> permissions = permissionRepository.findByUserName(userName); // 保存用戶權(quán)限到緩存中 session.saveUserPermissions(userName, permissions); return loginUser; } // ... // 緩存用戶權(quán)限到Redis public void saveUserPermissions(String userName, List<Permission> permissions) { String key = String.format("login:permission:%s", userName); HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash(); hashOperations.putAll(key, permissions.stream().collect( Collectors.toMap(p -> p.getMethod().concat(":").concat(p.getUri()), Permission::getName, (k1, k2) -> k2))); if (expireTime != null) { redisTemplate.expire(key, expireTime, TimeUnit.SECONDS); } }
- 用戶驗(yàn)證通過后,下發(fā)
userToken
,保存當(dāng)前登陸信息,緩存用戶授權(quán)列表 - 緩存授權(quán)列表時(shí),為了方便讀取使用hash方式保存為list,切勿直接將數(shù)組對(duì)象保存為一個(gè)object
2. 攔截請(qǐng)求
@Slf4j @Component public class AuthorizationFilter extends AbstractGatewayFilterFactory { @Autowired private Session session; @Override public GatewayFilter apply(Object config) { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); ServerHttpResponse response = exchange.getResponse(); String uri = request.getURI().getPath(); String method = request.getMethodValue(); // 1.從AuthenticationFilter中獲取userName String key = "X-User-Name"; if (!request.getHeaders().containsKey(key)) { response.setStatusCode(HttpStatus.FORBIDDEN); return response.setComplete(); } String userName = Objects.requireNonNull(request.getHeaders().get(key)).get(0); // 2.驗(yàn)證權(quán)限 if (!session.checkPermissions(userName, uri, method)) { log.info("用戶:{}, 沒有權(quán)限", userName); response.setStatusCode(HttpStatus.FORBIDDEN); return response.setComplete(); } return chain.filter(exchange); }; } }
- 第一步從取出身份認(rèn)證模塊傳遞的X-User-Name
- 第二步去緩存中檢查是否有相應(yīng)的權(quán)限
public boolean checkPermissions(String userName, String uri, String method) { String key = String.format("login:permission:%s", userName); String hashKey = String.format("%s:%s", method, uri); if (redisTemplate.opsForHash().hasKey(key, hashKey)){ return true; } String allKey = "login:permission:all"; // 權(quán)限列表中沒有則通過 return !redisTemplate.opsForHash().hasKey(allKey, hashKey); }
- 權(quán)限列表中沒有則通過 主要是放過一些沒有必要配置的公共資源,默認(rèn)都可以訪問的資源
login:permission:all
所有配置過的權(quán)限列表需要在程序啟動(dòng)時(shí)放入緩存,并需要保持?jǐn)?shù)據(jù)的更新
3. 鑒權(quán)Filter配置
spring: cloud: gateway: routes: - id: cloud-user uri: lb://cloud-user # 后端服務(wù)名 predicates: - Path=/user/** # 路由地址 filters: - name: AuthenticationFilter # 身份認(rèn)證 - name: AuthorizationFilter # 用戶鑒權(quán) - StripPrefix=1 # 去掉前綴
- 特別注意filter的順序,必須先做身份認(rèn)證后再進(jìn)行鑒權(quán)
- 如果有較多的路由都需要配置,可使用
default-filters
默認(rèn)Filter配置
三、其它問題
在做單元測(cè)試時(shí),如遇到如下錯(cuò)誤
nested exception is java.lang.NoClassDefFoundError: javax/validation/ValidationException
請(qǐng)升級(jí)依賴包版本:
<!--升級(jí)validation-api的版本--> <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.5.Final</version> </dependency> <dependency> <groupId>javax.validation</groupId> <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency>
四、完整代碼
以上就是Spring Cloud下實(shí)現(xiàn)用戶鑒權(quán)的方案的詳細(xì)內(nèi)容,更多關(guān)于Spring Cloud 用戶鑒權(quán)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
java使用poi讀取doc和docx文件的實(shí)現(xiàn)示例
這篇文章主要介紹了java使用poi讀取doc和docx文件的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03springboot注入yml配置文件 list報(bào)錯(cuò)的解決方案
這篇文章主要介紹了springboot注入yml配置文件 list報(bào)錯(cuò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08高并發(fā)下如何避免重復(fù)數(shù)據(jù)產(chǎn)生技巧
這篇文章主要為大家介紹了高并發(fā)下如何避免重復(fù)數(shù)據(jù)的產(chǎn)生技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07Java 兩種延時(shí)thread和timer詳解及實(shí)例代碼
這篇文章主要介紹了Java 兩種延時(shí)thread和timer詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-02-02Maven的pom.xml中resources標(biāo)簽的用法
本文主要介紹了Maven的pom.xml中resources標(biāo)簽的用法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07