以下這個案例將涉及到一個許可權管理場景,假設我們有一個內部管理系統,管理員可以動態變更使用者的許可權。我們將演示在使用者訪問某個資源時,許可權發生變更後,系統自動響應並及時反饋許可權的變化。
場景描述
在這個場景中,使用者有一個可以管理資源的頁面,比如一個“訂單管理系統”,使用者可以建立、編輯、刪除訂單。系統管理員可以動態更改使用者的許可權,比如撤銷其“刪除訂單”的許可權。當管理員更改使用者許可權後,前端頁面會監聽這個許可權的變化。透過瀏覽器端訂閱訊息佇列(MQ)的方式,實時地收到許可權變化通知,並根據許可權變動做出相應操作。
如果使用者的許可權被撤銷,前端會立即更新顯示。若使用者嘗試進行不允許的操作(例如刪除操作),系統會彈出許可權不足的提示。
技術要點:
Spring Boot作為後端框架。
RabbitMQ作為訊息佇列,實現許可權變更的通知。
前端使用WebSocket訂閱RabbitMQ佇列,監聽許可權變更。
使用Spring Security進行許可權控制。
複雜場景:許可權動態變更後,實時限制使用者操作(刪除按鈕隱藏,彈出許可權變更通知)。
解決方案
Spring Boot後端處理許可權變更: 當管理員修改了某個使用者的許可權,系統會將許可權變更的訊息傳送到RabbitMQ佇列,前端會透過WebSocket接收並實時更新頁面。
前端處理許可權變更: 使用者頁面透過WebSocket連線到後端,監聽許可權的變更。一旦收到許可權變更訊息,前端立即更新用戶界面。
MQ訂閱與前端動態變動: WebSocket與RabbitMQ的結合使得許可權變更可以及時反映在前端,無需手動重新整理。若使用者嘗試操作失去許可權的功能,會出現提示框告知使用者許可權不足。
實際程式碼實現
1. Spring Boot配置
配置RabbitMQ
在Spring Boot中配置RabbitMQ的連線和佇列。
application.yml:
spring: rabbitmq: host: localhost port: 5672 username: guest password: guest
建立訊息佇列配置類
import org.springframework.amqp.core.Queue; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class RabbitConfig { public static final String PERMISSION_CHANGE_QUEUE = "permission_change_queue"; @Bean public Queue permissionChangeQueue() { return new Queue(PERMISSION_CHANGE_QUEUE); } }
許可權變更服務
import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PermissionChangeService { @Autowired private RabbitTemplate rabbitTemplate; public void notifyPermissionChange(String userId) { // 傳送許可權變更訊息到佇列 rabbitTemplate.convertAndSend(RabbitConfig.PERMISSION_CHANGE_QUEUE, userId); } }
模擬許可權變更控制器
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.RestController; @RestController public class AdminController { @Autowired private PermissionChangeService permissionChangeService; @PreAuthorize("hasRole('ADMIN')") @PostMapping("/changePermission") public String changePermission(@RequestParam String userId, @RequestParam String newPermission) { // 修改資料庫中的許可權邏輯(省略) // 通知許可權變更 permissionChangeService.notifyPermissionChange(userId); return "Permissions updated for user: " + userId; } }
WebSocket配置類
import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS(); } }
WebSocket訊息傳送服務
import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; @Service public class WebSocketNotificationService { private final SimpMessagingTemplate messagingTemplate; public WebSocketNotificationService(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } public void notifyUser(String userId, String message) { messagingTemplate.convertAndSend("/topic/permission/" + userId, message); } }
消費者監聽許可權變更訊息
import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @Service public class PermissionChangeListener { @Autowired private WebSocketNotificationService webSocketNotificationService; @RabbitListener(queues = RabbitConfig.PERMISSION_CHANGE_QUEUE) public void handlePermissionChange(String userId) { // 通知前端許可權變更 webSocketNotificationService.notifyUser(userId, "Your permissions have been changed. Please refresh."); } }
2. 前端程式碼
WebSocket連線和許可權監聽
假設使用Vue.js前端框架。
WebSocket.js:
import SockJS from 'sockjs-client'; import Stomp from 'stompjs'; let stompClient = null; export function connect(userId, onPermissionChange) { const socket = new SockJS('/ws'); stompClient = Stomp.over(socket); stompClient.connect({}, function () { stompClient.subscribe('/topic/permission/' + userId, function (message) { onPermissionChange(JSON.parse(message.body)); }); }); } export function disconnect() { if (stompClient !== null) { stompClient.disconnect(); } }
Vue元件中的使用
<template> <div> <h1>Order Management</h1> <button v-if="canDelete" @click="deleteOrder">Delete Order</button> <p v-if="!canDelete" style="color:red">You do not have permission to delete orders</p> </div> </template> <script> import { connect, disconnect } from '@/websocket'; export default { data() { return { canDelete: true }; }, created() { const userId = this.$store.state.user.id; connect(userId, this.handlePermissionChange); }, beforeDestroy() { disconnect(); }, methods: { handlePermissionChange(message) { alert(message); this.canDelete = false; // 動態撤銷刪除許可權 }, deleteOrder() { if (this.canDelete) { // 傳送刪除請求 } else { alert('You do not have permission to delete this order.'); } } } }; </script>
執行流程
使用者登入系統,進入“訂單管理”頁面。
管理員在後臺更改使用者許可權,撤銷其“刪除訂單”的許可權。
後端透過RabbitMQ傳送許可權變更的訊息,訊息透過WebSocket通知前端使用者。
前端頁面接收到訊息後,自動禁用“刪除訂單”按鈕,使用者再也無法點選該按鈕。
若使用者試圖刪除訂單,系統會彈出許可權不足的提示。
總結
這個案例展示瞭如何使用Spring Boot結合RabbitMQ和WebSocket處理許可權動態變更的場景。透過實時監聽許可權變更並及時在前端做出反饋,可以確保使用者操作許可權的準確性,同時提高使用者體驗。