以下这个案例将涉及到一个权限管理场景,假设我们有一个内部管理系统,管理员可以动态变更用户的权限。我们将演示在用户访问某个资源时,权限发生变更后,系统自动响应并及时反馈权限的变化。
场景描述
在这个场景中,用户有一个可以管理资源的页面,比如一个"订单管理系统",用户可以创建、编辑、删除订单。系统管理员可以动态更改用户的权限,比如撤销其"删除订单"的权限。当管理员更改用户权限后,前端页面会监听这个权限的变化。通过浏览器端订阅消息队列(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:
yaml
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
创建消息队列配置类
java
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);
}
}
权限变更服务
java
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);
}
}
模拟权限变更控制器
java
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配置类
java
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消息发送服务
java
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);
}
}
消费者监听权限变更消息
java
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:
javascript
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组件中的使用
javascript
<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处理权限动态变更的场景。通过实时监听权限变更并及时在前端做出反馈,可以确保用户操作权限的准确性,同时提高用户体验。