pom.xml添加如下依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
修改config包中的WebMvcConfig类。
package com.gerrard.community.config;
import com.gerrard.community.controller.interceptor.DataInterceptor;
import com.gerrard.community.controller.interceptor.LoginRequiredInterceptor;
import com.gerrard.community.controller.interceptor.LoginTicketInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Autowired
private DataInterceptor dataInterceptor;
// @Autowired
// private LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css","**/*.js","**/*.png","**/*.jpg","**/*.jpeg");
// registry.addInterceptor(loginRequiredInterceptor)
// .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
registry.addInterceptor(dataInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}
在config包中新建SecurityConfig类。
package com.gerrard.community.config;
import com.gerrard.community.util.CommunityConstant;
import com.gerrard.community.util.CommunityUtil;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements CommunityConstant {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
@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
)
.anyRequest().permitAll()
.and().csrf().disable();
// 权限不够时的处理
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请求,进行退出处理.
// 覆盖它默认的逻辑,才能执行我们自己的退出代码.
//认证,认证的逻辑用自己的代码,哪些地方需要认证配置自己用api配一下,认证成功/失败用api配一下
//判断是否登录交给security,用的是securitycontext?
http.logout().logoutUrl("/securitylogout");
}
}
UserService类中添加getAuthorties方法。
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;
}
(1)在Interceptor包:LoginTicketInterceptor类中对方法进行修改。
package com.gerrard.community.controller.interceptor;
import com.gerrard.community.entity.LoginTicket;
import com.gerrard.community.entity.User;
import com.gerrard.community.service.MessageService;
import com.gerrard.community.service.UserService;
import com.gerrard.community.util.CookieUtil;
import com.gerrard.community.util.HostHolder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder;
@Autowired
private MessageService messageService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从cookie中获取凭证
String ticket= CookieUtil.getValue(request,"ticket");
if(ticket!=null){
//查询凭证
LoginTicket loginTicket=userService.findLoginTicket(ticket);
//检查凭证是否有效
//一般不会检测到过期,因为时间一到,cookie自动被销毁了?????
if(loginTicket!=null && loginTicket.getStatus()==0 && loginTicket.getExpired().after(new Date())){
//根据凭证查询用户
User user=userService.findUserById(loginTicket.getUserId());
//在本次请求中持有用户
hostHolder.setUser(user);
//构建用户认证的结果,并存入SecurityContext,以便于Security进行授权
Authentication authentication=new UsernamePasswordAuthenticationToken(
user,user.getPassword(),userService.getAuthorities(user.getId()));
SecurityContextHolder.setContext(new SecurityContextImpl(authentication));
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
User user=hostHolder.getUser();
if(user!=null && modelAndView !=null){
int unReadLetterCount=messageService.findLetterUnreadCount(user.getId(),null);
int unReadNoticeCount=messageService.findNoticeUnreadCount(user.getId(),null);
modelAndView.addObject("loginUser",user);
modelAndView.addObject("messageUnRead",unReadLetterCount+unReadNoticeCount);
}
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
// SecurityContextHolder.clearContext();
}
}
(2)LoginController修改logout方法。
@RequestMapping(path="/logout",method = RequestMethod.GET)
public String logout(@CookieValue("ticket")String ticket){
userService.logout(ticket);
SecurityContextHolder.clearContext();
return "redirect:/login";
}
(1)认证或权限控制触发的操作,借助Spring Security配置。
(2)每次登录或退出,设置SecurityContextHolder即可;接下来判断是否登录及判断是否有对应的权限,交给Spring Security。
(3)哪些地方需要认证/权限控制,借助Spring Security配置。
留个小问题:为什么在LoginTicketInterceptor类中的afterCompletion方法不清除SecurityContextHolder?
【和filter的执行顺序有关
前端:index.html页面中添加token信息(异步请求处理方法)index.js也要做修改
出现bug,需要修改notice.html前端页面和MessageController内容 https://blog.csdn.net/qq_42956993/article/details/110210921