文章目录
显示登录信息
拦截器示例
- 定义拦截器实现HandlerInterceptor 实现其三个方法
- preHandle // 在Controller之前执行 return false表示取消本次请求
- postHandle // 在Controller之后执行,TemplateEngine之前执行执行
- afterCompletion // 在TemplateEngine之后执行
@Component
public class AlphaInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
// 在Controller之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle: " + handler.toString());
return true;
}
// 在Controller之后执行,TemplateEngine之前执行执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
logger.debug("postHandle: " + handler.toString());
}
// 在TemplateEngine之后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
logger.debug("afterCompletion: " + handler.toString());
}
}
配置拦截器,指定拦截路径和排除的路径
- addInterceptor(alphaInterceptor) 添加要配置的拦截器
- excludePathPatterns 排除不要拦截的路径 多是静态资源
- addPathPatterns 添加需要拦截的路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AlphaInterceptor alphaInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
}
}
object handler 是拦截的目标
拦截器应用
登录的一次请求过程
1、编写一个工具类从请求中来获取cookie的值
public class CookieUtil {
public static String getValue(HttpServletRequest request, String name) {
if (request == null || name == null) {
throw new IllegalArgumentException("参数为空!");
}
//遍历得到的cookie集合找到值相等的cookie,获取其value
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cookie : cookies) {
if (cookie.getName().equals(name)) {
return cookie.getValue();
}
}
}
return null;
}
}
2、编写工具来持有用户信息
- 以线程为key存取值
- ThreadLocal可以把用户信息保存在线程中,用户发来的每一次请求启动的线程会保存用户的信息,当请求结束,保存的用户信息会被清除掉,方便我们在开发中获取用户登录信息,从而不需要每次取登录信息都需要从HttpServletRequest中取。
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user) {
users.set(user);
}
public User getUser() {
return users.get();
}
public void clear() {
users.remove();
}
}
3、编写LoginTicketInterceptor过滤器
- 请求之初,controller之前 保存用户登录信息
- 模板引擎调用之前 获取保存的用户信息
- 模板引擎调用之后 最后清除用户登录信息
@Component
public class LoginTicketInterceptor implements HandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
@Autowired(required = false)
private UserService userService;
@Autowired
private HostHolder hostHolder;
//请求之初,controller之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//从cookie中获取ticket
String ticket = CookieUtil.getValue(request, "ticket");
if(ticket!=null)
{
//检查其状态是否有效
LoginTicket loginTicket = userService.findLoginTicket(ticket);
if (loginTicket != null && loginTicket.getStatus() == 0 && loginTicket.getExpired().after(new Date())) {
// 根据凭证查询用户
User user = userService.queryUserById(loginTicket.getUserId());
// 在本次请求中持有用户
hostHolder.setUser(user);
}
}
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) {
modelAndView.addObject("loginUser", user);
}
}
//模板引擎调用之后 最后清理数据
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
hostHolder.clear();
}
}
4、配置拦截器,指定拦截路径和排除的路径
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AlphaInterceptor alphaInterceptor;
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}
5、修改页面
当用户登录之前显示注册登录,登录之后显示个人信息
账号设置
1、编写UserController处理设置请求
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping(value = "/setting",method = RequestMethod.GET)
public String getSettingPage()
{
return "/site/setting";
}
}
2、处理上传头像请求
保存图片路径暂时设置为本地服务器路径 为了方便更改,在properties中进行配置
community.path.upload=D:/work/upload
spring mvc MultipartFile是表现层对象 如果放到service处理上传业务会有耦合 因此放到controller处理上传操作
业务层只处理更新头像路径
//更新头像路径
public int updateHeader(int id,String header_url)
{
return userMapper.UpdateHeader(id,header_url);
}
如果上传多个文件就声明成一个数组
取文件后缀名
@RequestMapping(value = "/upload",method = RequestMethod.POST)
public String uploadHeader(MultipartFile headerImage, Model model)
{
if(headerImage==null)
{
model.addAttribute("headImageMsg","您还没有选择想要上传的图片!");
return "/site/setting";
}
//获取用户上传图片的后缀名
String fileName=headerImage.getOriginalFilename();
String suffix=fileName.substring(fileName.lastIndexOf("."));
if(StringUtils.isBlank(suffix))
{
model.addAttribute("headImageMsg","请选择正确的图片格式!");
return "/site/setting";
}
//生成随机文件名
fileName= CommunityUtil.generateUUID()+suffix;
//确定文件存放的路径
File dest=new File(uploadPath+"/"+fileName);
try {
//存储文件
headerImage.transferTo(dest);
} catch (IOException e) {
logger.error("上传文件失败"+e.getMessage());
throw new RuntimeException("服务器发生异常,上传文件失败",e);
}
//更新当前用户的头像路径(Web路径)
// http://localhost:8080/community/user/header/xxx.png
//从线程中获取用户信息
User user = hostHolder.getUser();
String url=domain+contextPath+"/user/header/"+fileName;
userService.updateHeader(user.getId(),url);
return "redirect:/index";
}
3、处理获取头像请求
java7的新语法
可以在try后面加()相当于finally
@RequestMapping(value = "/header/{fileName}",method = RequestMethod.GET)
public void getHeader(@PathVariable("fileName") String fileName, HttpServletResponse response)
{
//获取后缀
String suffix=fileName.substring(fileName.lastIndexOf("."));
//服务器存放路径
fileName=uploadPath+"/"+fileName;
//响应图片
response.setContentType("image/"+suffix);
try {
FileInputStream fileInputStream=new FileInputStream(fileName);
OutputStream outputStream = response.getOutputStream();
byte[] buffer=new byte[1024];
int b=0;
while ((b=fileInputStream.read(buffer))!=-1)
{
outputStream.write(buffer,0,b);
}
fileInputStream.close();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
logger.error("读取头像失败"+e.getMessage());
} catch (IOException e) {
logger.error("读取头像失败"+e.getMessage());
}
}
<!-- 上传头像 -->
<h6 class="text-left text-info border-bottom pb-2">上传头像</h6>
<form class="mt-5" method="post" enctype="multipart/form-data" th:action="@{/user/upload}">
<div class="form-group row mt-4">
<label for="head-image" class="col-sm-2 col-form-label text-right">选择头像:</label>
<div class="col-sm-10">
<div class="custom-file">
<input type="file" th:class="|custom-file-input ${headImageMsg!=null?'is-invalid':''}|"
id="head-image" name="headerImage" lang="es" required="">
<label class="custom-file-label" for="head-image" data-browse="文件">选择一张图片</label>
<div class="invalid-feedback" th:text="${headImageMsg}">
上传图片错误!
</div>
</div>
</div>
</div>
<div class="form-group row mt-4">
<div class="col-sm-2"></div>
<div class="col-sm-10 text-center">
<button type="submit" class="btn btn-info text-white form-control">立即上传</button>
</div>
</div>
</form>
开发修改密码功能:
1、在账号设置页面,填写原密码以及新密码,点击保存时将数据提交给服务器。
2、服务器检查原密码是否正确,若正确则将密码修改为新密码,并重定向到退出功能,强制用户重新登录。若错误则返回到账号设置页面,给与相应提示。
//需要向页面发送信息需要Model 参数要和输入框的name一致
@LoginRequired
@RequestMapping(value = "/updatePassword",method = RequestMethod.GET)
public String updatePassword(Model model,String oldPassword,String newPassword)
{
//@LoginRequired注解已经做了判断用户是否登录的功能 因此不需要在判断一次
//服务器检查原密码是否正确,若正确则将密码修改为新密码,
//先获取用户登录的user
User user = hostHolder.getUser();
oldPassword=CommunityUtil.MD5(oldPassword+user.getSalt());
if(oldPassword.equals(user.getPassword()))
{
//生成新的加密密码并且保存
newPassword=CommunityUtil.MD5(newPassword+user.getSalt());
userService.updatePassword(user.getId(),newPassword);
//并重定向到退出功能,强制用户重新登录。
//清楚线程持有的用户数据
hostHolder.clear();
return "redirect:/login";
}else {
model.addAttribute("passwordMsg","原密码错误,清重新输入!");
return "/site/setting";
}
}
使用自定义注解检查登录状态
在方法上加注解拦截
@Target 自定义注解作用域
@Retention 生效时间 编译还是运行
@Document 是否生成文档
@Inherited 如果某个类使用@Inherited修饰,则该类的子类将自动使用@Inherited修饰。 比如LoginRequired加上了这个@Inherited,那注解LoginRequired的类的子类也会自动注解上LoginRequired
1、创建自定义注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {
}
2、在需要拦截的方法上加上自定义的注解
3、定义拦截器
先判断拦截的是不是方法
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
HostHolder hostHolder;
//请求之初拦截
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//先判断拦截的是不是方法
if(handler instanceof HandlerMethod)
{
//获取方法
HandlerMethod handlerMethod=(HandlerMethod)handler;
Method method=handlerMethod.getMethod();
//通过反射
LoginRequired loginRequired=method.getAnnotation(LoginRequired.class);
//如果方法有注解并且登录信息为空就返回false取消请求
//并且重定向到login
if(loginRequired!=null&&hostHolder.getUser()==null)
{
response.sendRedirect(request.getContextPath()+"/login");
return false;
}
}
return true;
}
}
4、配置拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired
private AlphaInterceptor alphaInterceptor;
@Autowired
private LoginTicketInterceptor loginTicketInterceptor;
@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(alphaInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
.addPathPatterns("/register", "/login");
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
registry.addInterceptor(loginRequiredInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}