目录树
🌕开发社区登录注册模块
6.账号设置
上传文件前两点是硬性要求,图片存储到服务器上,之后换到云服务器上。
6.1 访问账号设置页面
6.1.1 UserController新建
@Controller
@RequestMapping("/user")//窄化请求
public class UserController {
//跳转设置页的请求
@RequestMapping(path = "/setting",method = RequestMethod.GET)
public String getSettingPage(){
return "/site/setting";
}
}
6.1.2 setting和index页面的修改
6.1.3 测试
前端页面确实能够跳转了!这一步很简单!
6.2 🍊上传
头像和获取
头像
6.2.1 application.properties
- 配置文件上传资源所在的位置
#配置上传文件资源的位置 开发的时候在本机,上线后在linux上
community.path.upload=D:/1920.1080/UPLOAD
6.2.2 UserService
直接去业务层写上传逻辑,与数据访问层没关系,因为不和数据库打交道。
业务层只处理更新文件路径的需求(改变头像),上传文件的事在controller表现层里面进行,因为MultipartFile是属于MVC中的,如果又在service操作它,耦合度比较高,不合适。
//更新用户头像
public int updateHeaderByUserId(int userId,String header){
return userMapper.updateHeader(userId, header);
}
6.2.3 UserController
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
//注入参数
@Value("${community.path.domain}")
private String domain;
@Value("${community.path.upload}")
private String uploadPath;
@Value("${server.servlet.context-path}")
private String contextPath;
@Autowired
private UserService userService;
@Autowired
private HostHolder hostHolder; //更新的是哪个用户的头像,可以从hostHolder来取,相当于session
//❤上传头像
@RequestMapping(path = "/upload",method = RequestMethod.POST)
public String uploadHeader(MultipartFile headerImage, Model model){
//1.先判断
if (headerImage == null){
model.addAttribute("error","您还没有选择图片!");
return "/site/setting";
}
//2.判断后缀合不合理
//存的图片不能按原始名称存,比如1.png,我存到硬盘就不能这名字;因为可能很多人传的名字都是1.png,就会冲突覆盖。一般随机生成名字,后缀不能改变
String filename = headerImage.getOriginalFilename(); //- 获取图片原始名
String suffix = filename.substring(filename.lastIndexOf("."));//- 截取文件名的最后一部分,“.”代表分割线,“lastIndexOf”代表取分割之后的,就是文件后缀名
if (StringUtils.isBlank(suffix)) { //判断空,因为可能文件就是没有后缀名
model.addAttribute("error", "图片格式不正确!");
return "/site/setting";
}
//3.生成随机文件名并带上后缀
filename = CommunityUtil.generateUUID() + suffix;
//确定文件存放的路径
File dest = new File(uploadPath + "/" + filename);
//存储文件 —— 将headerImage数据写入到这个dest中
try {
headerImage.transferTo(dest);
} catch (IOException e) {
logger.error("上传文件失败 " + e.getMessage());
throw new RuntimeException("上传文件失败,服务器发送异常!", e);
}
//4.更新当前用户头像路径 —— 存成功后,更新头像路径(web访问路径) 比如什么e盘那个文件夹是不行的,别人不在我们电脑上
//http://localhost:8080/community/user/header/xxx.png
User user = hostHolder.getUsers(); //直接从hostHolder获取 —— 持有用户信息,用于代替session对象
String headerUrl = domain + contextPath + "/user/header/" + filename; //新路径
userService.updateHeaderByUserId(user.getId(), headerUrl);
return "redirect:/index";
}
//❤获取头像 —— 和上面新路径相关
@RequestMapping(path = "/header/{filename}", method = RequestMethod.GET)
//为什么是void,因为比较特别;它向浏览器响应的不是一个网页或字符串,而是一个图片,是二进制的数据
//需要通过流手动向浏览器输出,所以返回值void,直接手动调用response往外面写就可以了
public void getHeader(@PathVariable("filename") String filename, HttpServletResponse response) {
//服务器存放路径
filename = uploadPath + "/" + filename;
//文件后缀解析
String suffix = filename.substring(filename.lastIndexOf("."));
//响应图片
response.setContentType("image/" + suffix);
try (
//这种方法就是业务完成后,不需要手动去close资源,会自动帮我们,前提是有close方法
OutputStream os = response.getOutputStream();//得到输出流
FileInputStream fis = new FileInputStream(filename);//得到输入流
) {
//OutputStream os = response.getOutputStream();//得到输出流
//FileInputStream fis = new FileInputStream(filename);//得到输入流
//有了输入流,就开始输出了
byte[] buffer = new byte[1024];
int a = 0;
while ((a = fis.read(buffer)) != -1) {
//不等于-1,表示读到数据了
os.write(buffer, 0, a);
}
} catch (IOException e) {
logger.error("读取头像失败 " + e.getMessage());
}
}
6.2.4 setting.html页面改造
6.2.5 测试
成功!
7.检查登录状态
比如用户知道设置头像的url路径,能直接通过地址栏跳转过去,这是不安全的!
所以拦截器能拦截这种行为,之前是通过配置文件在里面设置通行路径挨个指定,这次使用注解的方式!
- 想拦截哪个方法,就在方法上加一个注解,加了就拦截,不加不拦截
注解自己定义,并且怎么去识别 —— 关键
如果自己想定义个注解,就要用元注解定义我们自己注解-
@Target
—— 用来声明我们自定义的注解可以用在哪个位置,可以作用在哪个类型上;比如类上,方法上,属性上;-@Retention
—— 用来声明自定义注解所保留的时间或有效的时间;比如编译时有效,还是运行时有效-
@Document
—— 用来声明我们自定义注解在我们生成文档的时候要不要把它带进去-
@Inherited
—— 用来继承的,比如子类继承父类,父类上有这个自定义注解,子类要不要把这个注解也注入进来,由它指定;
Method.getDeclaredAnnotations() —— 获取这个方法之上所有的注解
Method.getAnnotation(Class《T》 annotationClass) —— 获取这个类型的注解
7.1 LoginRequired
第一步,先把注解定义好,这个比较简单。
新建一个包,annotation;
@Target(ElementType.METHOD) //注解可以写在方法之上,用来描述方法
@Retention(RetentionPolicy.RUNTIME) //表程序运行时有效
public @interface LoginRequired {
}
然后在响应的controller上加入这个注解,表示只有登录才能访问
加上以后,就要用拦截器来拦截带有注解的方法,拦截到方法以后,判断有没有登录。登陆了可以,没有就拒绝;然后写一个拦截器处理后续的逻辑;
7.2 LoginRequiredInterceptor
@Component
public class LoginRequiredInterceptor implements HandlerInterceptor {
@Autowired
private HostHolder hostHolder;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//先判断hander是不是属于方法;比如静态资源就不用拦截
if (handler instanceof HandlerMethod){
HandlerMethod handlerMethod = (HandlerMethod) handler; //转型
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
//判断loginRequired
if (loginRequired != null && hostHolder.getUsers() == null){
response.sendRedirect(request.getContextPath()+"/login");
return false;
}
}
return true;
}
}
7.3 WebMvcConfig
@Autowired
private LoginRequiredInterceptor loginRequiredInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginRequiredInterceptor) //全部拦截
.excludePathPatterns("/**/*.css","/**/*.js","/**/*.png","/**/*.jpg","/**/*.jpeg"); //放行的资源
}
7.4 测试
然后就会拦截并跳转到 登录页面:
然后: