用7.1中的springsecurity来进行如下的功能拓展
登录检查
废弃原来的登录检查拦截器
// @Autowired
// private LoginRequiredInterceptor loginRequiredInterceptor;
授权配置
对当前系统内包含的所有请求,分配对应的访问权限(普通用户、版主、管理员)
/**
* 权限: 普通用户
*/
String AUTHORITY_USER = "user";
/**
* 权限: 管理员
*/
String AUTHORITY_ADMIN = "admin";
/**
* 权限: 版主
*/
String AUTHORITY_MODERATOR = "moderator";
认证方案
配置
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {
@Override
public void configure(WebSecurity web) throws Exception {
//忽略静态资源的拦截
web.ignoring().antMatchers("/resources/**");
}
//忽略第二步重写author,因为我们现在的项目已经写好了相应的内容,要绕过去
//第三步
@Override
protected void configure(HttpSecurity http) throws Exception {
//授权
//每个人开发的时候命名路径不一样,要注意
http.authorizeRequests()
.antMatchers(/* 以下路径需要有hasAnyAuthority()中的权限才能访问*/
"/user/setting",
"/user/upload",
"/discuss/add",
"/comment/add/**",
"/letter/**",
"/like",
"/follow",
"/notice/**",
"/unfollow"
)
.hasAnyAuthority(
AUTHORITY_ADMIN,
AUTHORITY_USER,
AUTHORITY_MODERATOR
)
.anyRequest()/* 其他任意的路径*/
.permitAll()/* 都可以访问*/
.and().csrf().disable();//图省事就把csrf给取消掉了,这样就不会给页面生成凭证了
//权限不够时候的处理
http.exceptionHandling()
.authenticationEntryPoint(new AuthenticationEntryPoint() {
//没有登录
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
//如何判断是否是异步还是同步请求
String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {
//满足就是异步请求
//异步时的处理
response.setContentType("application/plain;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(CommunityUtil.getJSONString(403, "尚未登录"));
} else {
//非异步请求就直接重定向回去
response.sendRedirect(request.getContextPath() + "/login");
}
}
}
)
.accessDeniedHandler(new AccessDeniedHandler() {
/* 权限不足时的处理*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
//如何判断是否是异步还是同步请求
String xRequestedWith = request.getHeader("x-requested-with");
if ("XMLHttpRequest".equals(xRequestedWith)) {
//满足就是异步请求
//异步时的处理
response.setContentType("application/plain;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.write(CommunityUtil.getJSONString(403, "无访问此功能的权限"));
} else {
//非异步请求就直接重定向去权限不足的页面
response.sendRedirect(request.getContextPath() + "/denied");
}
}
});
//默认情况下security底层自动拦截/logout这个路径的请求,进行退出处理
//我们要覆盖它底层默认的逻辑,才能执行我们自己的退出代码
http.logout().logoutUrl("/securityLogout");//把它底层默认的拦截路劲给改了,这样就能执行我们自己的退出路径
}
}
新增一个根据用户判断用户的权限的方法
//根据用户判断用户的权限
public Collection<? extends GrantedAuthority> getAuthorities(int userId){
//
User user = this.findUserById(userId);
List<GrantedAuthority> list = new ArrayList<>();
list.add(new GrantedAuthority() {
@Override
public String getAuthority() {
//返回权限的字符串
switch (user.getType()){
case 1:
return AUTHORITY_ADMIN;
case 2:
return AUTHORITY_MODERATOR;
default:
return AUTHORITY_USER;
}
}
});
return list;
}
}
在LoginTicketInterceptor类preHandle方法里hostHolder.setUser(user);之后添加
//构建用户认证的结果,并存入SecurityContext,以便于Security进行授权,
Authentication authentication = new UsernamePasswordAuthenticationToken(
user,user.getPassword(),userService.getAuthorities(user.getId()));
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
afterCompletion方法里hostHolder.clear();之后添加
SecurityContextHolder.clearContext();
在LoginController类logout方法里userService.logout(ticket)后添加
SecurityContextHolder.clearContext();
CSRF配置
spring security是用token来解决CSRF攻击。开启security自动会在表单上生成token,但是异步请求没有form,需要自定义配置。
异步的时候没有表单,没法处理,那就要自己另外处理。例如发帖时,在index.html里头部添加
<!-- 访问该页面时,在此处生成CSRF令牌-->
<!-- <meta name="_csrf" th:content="${_csrf.token}">-->
<!-- 把表单传给服务器-->
<!-- <meta name="_csrf_header" th:content="${_csrf.headerName}">-->
异步请求传数据不是通过请求体,是通过请求消息头,如上设置之后spring security会生成csrf的key和value。在index.js里添加:
发送ajax请求之前,把csrf令牌设置到请求的消息头中
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e,xhr,options) {
xhr.setRequestHeader(header,token);
})
每个都要处理,麻烦就直接注释掉了Index和js中的csrf、
.and().csrf().disable();//图省事就把csrf给取消掉了,这样就不会给页面生成凭证了
之前的坑:
如果当前用户没有私心和赞报错:
把messageVO 放在判断之后。显示处理完以后要去前端看看!动脑子!前端页面也要把bug给改过来。
如果是followNotice.message!=null的话会直接因为followNotice为null而报错,所以要改成th:if="${followNotice!=null}"
if (message != null) {
Map<String, Object> messageVO = new HashMap<>();
messageVO.put("message", message);
String content = HtmlUtils.htmlUnescape(message.getContent());
Map<String, Object> data = JSONObject.parseObject(content, HashMap.class);
messageVO.put("user", userService.findUserById((Integer) data.get("userId")));
messageVO.put("entityType", data.get("entityType"));
messageVO.put("entityId", data.get("entityId"));
// System.out.println("调试调试调试"+userService.findUserById((Integer) data.get("userId")));
int count = messageService.findNoticeCount(user.getId(), TOPIC_FOLLOW);
messageVO.put("count", count);
int unread = messageService.findNoticeUnreadCount(user.getId(), TOPIC_FOLLOW);
messageVO.put("unread", unread);
model.addAttribute("followNotice", messageVO);
}
<!--关注类通知-->
<li class="media pb-3 pt-3 mb-3 border-bottom position-relative" th:if="${followNotice!=null}">