基于Spring boot和Vue3的博客平台:系统通知、评论回复通知、专栏更新通知、用户角色与权限管理模块
目录
一、系统通知、评论回复通知、专栏更新通知
1.前端通知展示
2.后端通知实现
3.发送评论回复通知
4.发送专栏更新通知
二、实时通知与邮件通知
1.实时通知
三、用户角色与权限管理
1.角色与权限数据模型
2.分配角色与权限
3.基于角色的访问控制
在这篇博客中,我们将介绍如何为我们的博客平台实现通知功能。这将包括系统通知、评论回复通知以及专栏更新通知。
一、系统通知、评论回复通知、专栏更新通知
1.前端通知展示
在前端项目中,创建一个新的 Notification.vue
组件。在这个组件中,我们将展示用户收到的所有通知。
<template>
<div class="notifications">
<h3>Notifications</h3>
<ul>
<li v-for="notification in notifications" :key="notification.id">
{{ notification.content }}
</li>
</ul>
</div>
</template>
<script>
export default {
data() {
return {
notifications: [],
};
},
async mounted() {
await this.fetchNotifications();
},
methods: {
async fetchNotifications() {
// ... 获取通知的逻辑 ...
},
},
};
</script>
2.后端通知实现
在后端项目中,创建一个新的 Notification
实体类,用于存储通知。
@Entity
public class Notification {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
private User recipient;
private String content;
// ... getter 和 setter ...
}
接下来,在 NotificationRepository
中,添加一个方法,根据接收者查找通知。
public interface NotificationRepository extends JpaRepository<Notification, Long> {
List<Notification> findByRecipientId(Long recipientId);
}
接下来,在 NotificationController
中添加获取用户通知的接口。
@RestController
@RequestMapping("/api/notifications")
public class NotificationController {
@Autowired
private NotificationRepository notificationRepository;
@GetMapping
public ResponseEntity<?> getUserNotifications(Principal principal) {
// ... 获取用户通知的逻辑 ...
}
}
3.发送评论回复通知
在评论回复功能中,当用户回复了某篇文章或其他评论时,我们可以为被回复的用户发送一条通知。在 CommentController
的 submitArticleComment
方法中,添加发送通知的逻辑。
@PostMapping
public ResponseEntity<?> submitArticleComment(@PathVariable Long articleId, @RequestBody CommentDto commentDto, Principal principal) {
// ... 提交评论的逻辑 ...
// 发送通知
Notification notification = new Notification();
notification.setRecipient(/* 被回复的用户 */);
notification.setContent(/* 通知内容 */);
notificationRepository.save(notification);
return ResponseEntity.ok(comment);
}
4.发送专栏更新通知
当专栏作者发布新文章时,我们可以为订阅了该专栏的用户发送一条通知。首先,我们需要在 SubscriptionRepository
中添加一个方法,根据专栏 ID 查找订阅者。
public interface SubscriptionRepository extends JpaRepository<Subscription, Long> {
List<Subscription> findByColumnId(Long columnId);
}
接下来,在 ArticleController
的 createArticle
方法中,添加发送通知的逻辑。
@PostMapping
public ResponseEntity<?> createArticle(@RequestBody ArticleDto articleDto, Principal principal) {
// ... 创建文章的逻辑 ...
// 获取专栏订阅者
List<Subscription> subscriptions = subscriptionRepository.findByColumnId(article.getColumn().getId());
// 遍历订阅者,发送通知
for (Subscription subscription : subscriptions) {
Notification notification = new Notification();
notification.setRecipient(subscription.getUser());
notification.setContent("专栏 [" + article.getColumn().getName() + "] 有新文章发布: " + article.getTitle());
notificationRepository.save(notification);
}
return ResponseEntity.ok(article);
}
现在,每当专栏作者发布新文章时,系统将自动向订阅了该专栏的用户发送更新通知。这样,用户可以随时关注他们感兴趣的专栏,并获取最新文章的信息。
通过以上实现,我们已经为博客平台添加了系统通知、评论回复通知以及专栏更新通知功能。用户可以在通知页面查看他们收到的所有通知,这将极大地提高用户的参与度和互动体验。你可以根据需要进一步完善和扩展通知功能,例如提供邮件通知、实时通知等。
二、实时通知与邮件通知
在本节中,我们将为博客平台添加实时通知功能,以便用户在浏览网站时立即收到新通知。我们还将实现邮件通知功能,以便用户在收到关键通知时通过电子邮件获得提醒。
1.实时通知
为了实现实时通知,我们将使用 WebSocket 技术。首先,在后端项目中引入 Spring Boot WebSocket 依赖项。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
接下来,在 WebSocketConfig
类中配置 WebSocket。
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(notificationHandler(), "/api/ws/notifications")
.setAllowedOrigins("*");
}
@Bean
public WebSocketHandler notificationHandler() {
return new NotificationHandler();
}
}
然后,创建一个 NotificationHandler
类,用于处理 WebSocket 通信。
public class NotificationHandler extends TextWebSocketHandler {
// 存储 WebSocket 会话
private final Map<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// ... 建立连接后的逻辑 ...
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// ... 处理接收到的消息 ...
}
// 发送实时通知
public void sendNotification(Notification notification) {
// ... 发送通知的逻辑 ...
}
}
在前端项目中,使用 WebSocket
API 连接到后端 WebSocket 服务器,并监听新通知。
// Notification.vue
export default {
data() {
return {
// ...
socket: null,
};
},
mounted() {
this.connectWebSocket();
},
methods: {
// ...
connectWebSocket() {
this.socket = new WebSocket("ws://localhost:8080/api/ws/notifications");
this.socket.addEventListener("message", this.handleNotification);
},
handleNotification(event) {
const notification = JSON.parse(event.data);
this.notifications.push(notification);
},
},
};
现在,每当用户收到新通知时,他们将立即在前端收到实时提醒。
2.邮件通知
为了实现邮件通知功能,我们将使用 JavaMailSender 发送电子邮件。首先,在后端项目中引入 Spring Boot Mail 依赖项。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
接下来,在 application.properties
文件中配置邮件发送相关属性。
spring.mail.host=smtp.example.com
spring.mail.port=587
spring.mail.username=your_email@example.com
spring.mail.password=your_email_password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
创建一个 EmailService
类,用于发送邮件通知。
@Service
public class EmailService {
@Autowired
private JavaMailSender javaMailSender;
/**
* 发送邮件通知
* @param to 收件人
* @param subject 邮件主题
* @param content 邮件内容
*/
public void sendNotificationEmail(String to, String subject, String content) {
SimpleMailMessage message = new SimpleMailMessage();
message.setTo(to);
message.setSubject(subject);
message.setText(content);
try {
javaMailSender.send(message);
} catch (MailException e) {
// 如果发送邮件失败,记录错误信息
System.err.println("发送邮件失败: " + e.getMessage());
}
}
}
现在,我们已经创建了一个 EmailService
类,用于发送邮件通知。你可以在需要发送邮件通知的地方调用此服务。例如,在发送评论回复通知时,可以同时发送一封邮件提醒被回复的用户:
@PostMapping
public ResponseEntity<?> submitArticleComment(@PathVariable Long articleId, @RequestBody CommentDto commentDto, Principal principal) {
// ... 提交评论的逻辑 ...
// 发送通知
Notification notification = new Notification();
notification.setRecipient(/* 被回复的用户 */);
notification.setContent(/* 通知内容 */);
notificationRepository.save(notification);
// 发送邮件通知
String recipientEmail = /* 被回复用户的邮箱地址 */;
String emailSubject = "您在博客平台上收到了一条新评论";
String emailContent = "您在文章《" + article.getTitle() + "》上收到了一条新评论:" + commentDto.getContent();
emailService.sendNotificationEmail(recipientEmail, emailSubject, emailContent);
return ResponseEntity.ok(comment);
}
通过以上实现,我们已经为博客平台添加了实时通知和邮件通知功能。用户在浏览网站时将实时收到新通知,并在关键通知发生时通过电子邮件获得提醒。这将极大地提高用户的参与度和互动体验。
三、用户角色与权限管理
为了实现不同类型的用户具有不同的权限,我们需要引入角色与权限管理。本节将介绍如何为用户分配角色,并根据角色分配权限。
1.角色与权限数据模型
首先,我们创建两个实体类:Role
和 Permission
,表示用户角色和权限。一个角色可以拥有多个权限,一个权限可以分配给多个角色。
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(name = "role_permission",
joinColumns = @JoinColumn(name = "role_id"),
inverseJoinColumns = @JoinColumn(name = "permission_id"))
private Set<Permission> permissions;
}
@Entity
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy = "permissions")
private Set<Role> roles;
}
接下来,更新 User
实体类,添加与 Role
的多对多关联。
@Entity
public class User {
// ... 其他字段 ...
@ManyToMany
@JoinTable(name = "user_role",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id"))
private Set<Role> roles;
}
2.分配角色与权限
创建一个 RoleRepository
和一个 PermissionRepository
,用于操作角色和权限数据。
public interface RoleRepository extends JpaRepository<Role, Long> {
}
public interface PermissionRepository extends JpaRepository<Permission, Long> {
}
接下来,创建一个初始化角色与权限的方法。例如,我们可以创建一个管理员角色,拥有所有权限,以及一个普通用户角色,只拥有部分权限。
@Service
public class InitService {
@Autowired
private RoleRepository roleRepository;
@Autowired
private PermissionRepository permissionRepository;
public void initRolesAndPermissions() {
// 创建权限
Permission createArticle = new Permission("CREATE_ARTICLE");
Permission editArticle = new Permission("EDIT_ARTICLE");
Permission deleteArticle = new Permission("DELETE_ARTICLE");
permissionRepository.saveAll(Arrays.asList(createArticle, editArticle, deleteArticle));
// 创建普通用户角色
Role userRole = new Role("USER");
userRole.setPermissions(Collections.singleton(createArticle));
roleRepository.save(userRole);
// 创建管理员角色
Role adminRole = new Role("ADMIN");
adminRole.setPermissions(new HashSet<>(Arrays.asList(createArticle, editArticle, deleteArticle)));
roleRepository.save(adminRole);
}
}
3.基于角色的访问控制
为了实现基于角色的访问控制,我们需要在 SecurityConfig
类中配置权限控制规则。例如,只有管理员才能编辑和删除文章。
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// ... 其他配置 ...
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserDetailsService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
// ... 其他配置 ...
.authorizeRequests()
.antMatchers(HttpMethod.POST, "/api/articles").hasAnyRole("USER", "ADMIN")
.antMatchers(HttpMethod.PUT, "/api/articles/**").hasRole("ADMIN")
.antMatchers(HttpMethod.DELETE, "/api/articles/**").hasRole("ADMIN")
.antMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().permitAll();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationEntryPoint authenticationEntryPoint() {
return (request, response, authException) -> {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
};
}
@Bean
public AccessDeniedHandler accessDeniedHandler() {
return (request, response, accessDeniedException) -> {
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");
};
}
}
在这个重写的基于角色的访问控制配置中,我们首先配置了用户创建文章的权限,允许具有 USER
和 ADMIN
角色的用户创建文章。接下来,我们为文章的编辑和删除操作设置了权限,仅允许具有 ADMIN
角色的用户进行这些操作。
我们还为 /api/admin/**
设置了访问权限,只允许具有 ADMIN
角色的用户访问该路径下的所有资源。
此外,我们添加了一个 AuthenticationEntryPoint
和一个 AccessDeniedHandler
,用于自定义身份验证失败和访问被拒绝时的错误响应。
这样,我们已经实现了基于角色的访问控制,确保了不同类型的用户只能访问其被授权的资源。