目录
一、系统通知、评论回复通知、专栏更新通知
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;@ManyToOneprivate 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 {@Autowiredprivate NotificationRepository notificationRepository;@GetMappingpublic 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 {@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {registry.addHandler(notificationHandler(), "/api/ws/notifications").setAllowedOrigins("*");}@Beanpublic WebSocketHandler notificationHandler() {return new NotificationHandler();}
}
然后,创建一个 NotificationHandler
类,用于处理 WebSocket 通信。
public class NotificationHandler extends TextWebSocketHandler {// 存储 WebSocket 会话private final Map<Long, WebSocketSession> sessions = new ConcurrentHashMap<>();@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// ... 建立连接后的逻辑 ...}@Overrideprotected 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 {@Autowiredprivate 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 {@Autowiredprivate RoleRepository roleRepository;@Autowiredprivate 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 {// ... 其他配置 ...@Autowiredprivate CustomUserDetailsService customUserDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(customUserDetailsService);}@Overrideprotected 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();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic AuthenticationEntryPoint authenticationEntryPoint() {return (request, response, authException) -> {response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");};}@Beanpublic AccessDeniedHandler accessDeniedHandler() {return (request, response, accessDeniedException) -> {response.sendError(HttpServletResponse.SC_FORBIDDEN, "Access Denied");};}
}
在这个重写的基于角色的访问控制配置中,我们首先配置了用户创建文章的权限,允许具有 USER
和 ADMIN
角色的用户创建文章。接下来,我们为文章的编辑和删除操作设置了权限,仅允许具有 ADMIN
角色的用户进行这些操作。
我们还为 /api/admin/**
设置了访问权限,只允许具有 ADMIN
角色的用户访问该路径下的所有资源。
此外,我们添加了一个 AuthenticationEntryPoint
和一个 AccessDeniedHandler
,用于自定义身份验证失败和访问被拒绝时的错误响应。
这样,我们已经实现了基于角色的访问控制,确保了不同类型的用户只能访问其被授权的资源。