https://github.com/thymeleaf/thymeleaf-extras-springsecurity
- 功能实现
- 点击 置顶,修改帖子的类型。
- 点击 “加精”、“删除”,修改帖子的状态。
- 权限管理
- 版主可以执行“置顶”、“加精” 操作。
- 管理员可以执行“删除”操作。
- 按钮显示
- 版主可以看到“置顶”、“加精” 按钮。
- 管理员可以看到“删除” 按钮。
一、功能实现
DAO层
DiscussPostMapper接口中添加以下两个方法:
置顶操作:即把帖子的 type 字段改为 1
加精操作:即把帖子的 status 字段改为 1
删除操作:即把帖子的 status 字段改为 2
int updateType(int id, int type);
int updateStatus(int id, int status);
添加相应的mapper:
discusspost-mapper.xml
<update id="updateType">
update discuss_post set type = #{type} where id = #{id}
</update>
<update id="updateStatus">
update discuss_post set status = #{status} where id = #{id}
</update>
Service
DiscussPostService.java
public int updateType(int id, int type) {
return discussPostMapper.updateType(id, type);
}
public int updateStatus(int id, int status) {
return discussPostMapper.updateStatus(id, status);
}
Controller
DiscussPostService.java
A)置顶
// 置顶
@RequestMapping(path = "/top", method = RequestMethod.POST)
@ResponseBody
public String setTop(int id) {
discussPostService.updateType(id, 1);
// 触发发帖事件
Event event = new Event()
.setTopic(TOPIC_PUBLISH)
.setUserId(hostHolder.getUser().getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(id);
eventProducer.fireEvent(event);
return CommunityUtil.getJSONString(0);
}
1、置顶操作即是修改帖子的type=1,调用discussPostService.updateType() 方法
2、由于帖子的数据修改了,因此要更新 Elasticsearch 中帖子的数据,只需要触发发帖事件即可。(即利用消息队列将数据异步提交到Elasticsearch 中)
3、返回Json字符串
B)加精操作
与置顶操作逻辑类似
// 加精
@RequestMapping(path = "/wonderful", method = RequestMethod.POST)
@ResponseBody
public String setWonderful(int id) {
discussPostService.updateStatus(id, 1);
// 触发发帖事件
Event event = new Event()
.setTopic(TOPIC_PUBLISH)
.setUserId(hostHolder.getUser().getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(id);
eventProducer.fireEvent(event);
// 计算帖子分数
String redisKey = RedisKeyUtil.getPostScoreKey();
redisTemplate.opsForSet().add(redisKey, id);
return CommunityUtil.getJSONString(0);
}
C)删除操作
// 删除
@RequestMapping(path = "/delete", method = RequestMethod.POST)
@ResponseBody
public String setDelete(int id) {
discussPostService.updateStatus(id, 2);
// 触发删帖事件
Event event = new Event()
.setTopic(TOPIC_DELETE)
.setUserId(hostHolder.getUser().getId())
.setEntityType(ENTITY_TYPE_POST)
.setEntityId(id);
eventProducer.fireEvent(event);
return CommunityUtil.getJSONString(0);
}
不同的是要新增一个 删帖事件:
1、定义删帖主题
CommunityConstant接口
/**
* 主题: 删帖
*/
String TOPIC_DELETE = "delete";
2、消费删帖事件,这是删帖事件触发时,所执行的逻辑
EventConsumer类
// 消费删帖事件
@KafkaListener(topics = {TOPIC_DELETE})
public void handleDeleteMessage(ConsumerRecord record) {
if (record == null || record.value() == null) {
logger.error("消息的内容为空!");
return;
}
Event event = JSONObject.parseObject(record.value().toString(), Event.class);
if (event == null) {
logger.error("消息格式错误!");
return;
}
// 主要是这个方法的调用,前面判断的逻辑与其它消费事件一样
elasticsearchService.deleteDiscussPost(event.getEntityId());
}
前端
置顶、加精、删除 三个按钮,点击后,由异步提交post请求
discuss-detail.html
<div class="float-right">
<input type="hidden" id="postId" th:value="${post.id}">
<button type="button" class="btn btn-danger btn-sm" id="topBtn"
th:disabled="${post.type==1}" sec:authorize="hasAnyAuthority('moderator')">置顶</button>
<button type="button" class="btn btn-danger btn-sm" id="wonderfulBtn"
th:disabled="${post.status==1}" sec:authorize="hasAnyAuthority('moderator')">加精</button>
<button type="button" class="btn btn-danger btn-sm" id="deleteBtn"
th:disabled="${post.status==2}" sec:authorize="hasAnyAuthority('admin')">删除</button>
</div>
1、添加id 属性
2、th:disabled="${post.type==1}“ 指帖子加精后,按钮变灰(即不能再点击)
3、<input type="hidden" id="postId" th:value="${post.id}">
隐藏框,用于向服务器提交 帖子的id. (具体操作在discuss.js 中)
discuss.js
$(function(){
$("#topBtn").click(setTop);
$("#wonderfulBtn").click(setWonderful);
$("#deleteBtn").click(setDelete);
});
// 置顶
function setTop() {
$.post(
CONTEXT_PATH + "/discuss/top",
{"id":$("#postId").val()},
function(data) {
data = $.parseJSON(data);
if(data.code == 0) {
$("#topBtn").attr("disabled", "disabled"); // 置顶操作成功:disabled属性设置成 disabled,即不能再次点击此按钮
} else {
alert(data.msg); // 失败后返回提示
}
}
);
}
// 加精
function setWonderful() {
$.post(
CONTEXT_PATH + "/discuss/wonderful",
{"id":$("#postId").val()},
function(data) {
data = $.parseJSON(data);
if(data.code == 0) {
$("#wonderfulBtn").attr("disabled", "disabled");
} else {
alert(data.msg);
}
}
);
}
// 删除
function setDelete() {
$.post(
CONTEXT_PATH + "/discuss/delete",
{"id":$("#postId").val()},
function(data) {
data = $.parseJSON(data);
if(data.code == 0) {
location.href = CONTEXT_PATH + "/index"; // 删除成功直接跳转到首页
} else {
alert(data.msg);
}
}
);
}
1、$(function(){ });
html页面加载完毕后,javascript得到标签,再动态绑定事件。
2、$("#topBtn").click(setTop);
即:html页面加载完毕后,得到 topBtn 按钮,绑定单击事件,单击时调用setTop函数
3、setTop函数
该函数即利用 $.post( ) 发送一个异步的post的请求。
第一个参数:提交路径;第二个参数:向服务器提交的参数,该参数是获取html页面 的 id="postId"的值;第三个参数是回调函数,处理返回的json字符串,使用 $.parseJSON( ) 解析。
权限管理
SecurityConfig 中增加相应的授权配置即可
@Override
protected void configure(HttpSecurity http) throws Exception {
// 授权
http.authorizeRequests()
.antMatchers(
"/user/setting",
"/user/upload",
"/discuss/add",
"/comment/add/**",
"/letter/**",
"/notice/**",
"/like",
"/follow",
"/unfollow"
)
.hasAnyAuthority(
AUTHORITY_USER,
AUTHORITY_ADMIN,
AUTHORITY_MODERATOR
)
// 置顶、加精 需要 AUTHORITY_MODERATOR
.antMatchers(
"/discuss/top",
"/discuss/wonderful"
)
.hasAnyAuthority(
AUTHORITY_MODERATOR
)
// 删除 需要 AUTHORITY_ADMIN
.antMatchers(
"/discuss/delete",
)
.hasAnyAuthority(
AUTHORITY_ADMIN
)
.anyRequest().permitAll()
.and().csrf().disable(); // 取消了防止CSRF攻击
按钮显示
该操作使用的是thymeleaf-extras-springsecurity,相关文档:https://github.com/thymeleaf/thymeleaf-extras-springsecurity
1、导包
pom.xml
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
2、html上要声明命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
3、相应按钮处添加
sec:authorize="hasAnyAuthority('moderator')" 指仅当用户的权限是moderator是,才会显示此按钮
比如置顶按钮处:
<button type="button" class="btn btn-danger btn-sm" id="topBtn"
th:disabled="${post.type==1}" sec:authorize="hasAnyAuthority('moderator')">置顶</button>