JavaWeb

文件上传

图片存储方案

实现图片上传服务,需要有存储的支持,那么我们的解决方案有以下几种:

  1. 直接将图片保存到服务的硬盘 优点:开发便捷,成本低 缺点:扩容困难

  2. 使用分布式文件系统进行存储(Minio FastDFS) 优点:容易实现扩容 缺点:开发复杂度稍大

  3. 使用第三方的存储服务(阿里云 华为云 七牛云) 优点:开发简单,拥有强大功能,免维护 缺点:付费

在本套课程中选用阿里云的OSS服务进行图片存储

阿里云OSS

官网地址:https://www.aliyun.com/product/oss

注册登录

充值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

开通服务

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建空间

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建秘钥

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

官方SDK

https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11174283.6.946.425e7da24qVtmx

<!--阿里云-->
<dependency>
    <groupId>com.aliyun.oss</groupId>
    <artifactId>aliyun-sdk-oss</artifactId>
    <version>3.10.2</version>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
    <version>4.0.6</version>
</dependency>
package com.itheima.test;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import org.junit.jupiter.api.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class OssTest {

    @Test
    public void testUpload() throws FileNotFoundException {
        // 区域
        String endpoint = "http://oss-cn-beijing.aliyuncs.com";

        // 秘钥
        String accessKeyId = "LTAI5tNmH22y9C7AxdFEgdNv";
        String accessKeySecret = "AHGC0JSnBuYtYg3BnNvyTuwlwyl27n";

        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);

        // 上传文件
        InputStream inputStream = new FileInputStream("D:\\upload\\jr.jpg");

        //参数1: 桶名字  参数2: 图片上传后名字   参数3: 图片流
        ossClient.putObject("tlias-gxm-191", "haha.jpg", inputStream);

        // 关闭OSSClient。
        ossClient.shutdown();
    }
}

工具类抽取

创建配置类

在项目中创建com.itheima.util.OssProperties

package com.itheima.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.boot.context.properties.ConfigurationProperties;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ConfigurationProperties(prefix = "oss")//读取配置信息
public class OssProperties {
    private String key; //访问key
    private String secret;//访问秘钥
    private String endpoint;//端点
    private String bucket;//桶名
}

创建工具类

在项目中创建com.itheima.util.OssTemplate

package com.itheima.util;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.ObjectMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

//阿里存储工具类
@Component
@Import(OssProperties.class)//导入,相当于将OssProperties对象放入容器
public class OssTemplate {

    @Autowired
    private OssProperties ossProperties;

    //文件上传
    public String upload(String fileName, InputStream inputStream) {
        //创建客户端
        OSS ossClient = new OSSClientBuilder().build(ossProperties.getEndpoint(), ossProperties.getKey(), ossProperties.getSecret());

        //设置文件最终的路径和名称
        String objectName = "images/" + new SimpleDateFormat("yyyy/MM/dd").format(new Date())
                + "/" + System.currentTimeMillis() + fileName.substring(fileName.lastIndexOf("."));

        //meta设置请求头,解决访问图片地址直接下载
        ObjectMetadata meta = new ObjectMetadata();
        meta.setContentType(getContentType(fileName.substring(fileName.lastIndexOf("."))));

        //上传
        ossClient.putObject(ossProperties.getBucket(), objectName, inputStream, meta);

        //关闭客户端
        ossClient.shutdown();

        return "https://" + ossProperties.getBucket() + "." + ossProperties.getEndpoint() + "/" + objectName;
    }

    //文件后缀处理
    private String getContentType(String FilenameExtension) {
        if (FilenameExtension.equalsIgnoreCase(".bmp")) {
            return "image/bmp";
        }
        if (FilenameExtension.equalsIgnoreCase(".gif")) {
            return "image/gif";
        }
        if (FilenameExtension.equalsIgnoreCase(".jpeg") ||
                FilenameExtension.equalsIgnoreCase(".jpg") ||
                FilenameExtension.equalsIgnoreCase(".png")) {
            return "image/jpg";
        }
        return "image/jpg";
    }
}

添加配置

在配置文件application.yml中添加oss的配置注意:安装自己的阿里云配置修改

oss:
  key: LTAI5tRYNe7GUtiqsktTwfVZ
  secret: EcKCLKzCIuTH7b2gVtfCoMbwm9Etbr
  endpoint: oss-cn-beijing.aliyuncs.com
  bucket: tanhua-gxm

测试

在测试类中进行测试com.itheima.test.OssTemplateTest

package com.itheima.test;

import com.itheima.util.OssTemplate;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

@SpringBootTest
@Slf4j
public class OssTemplateTest {
    @Autowired
    private OssTemplate ossTemplate;

    @Test
    public void testFileUpload() throws FileNotFoundException {
        String filePath = ossTemplate.upload("1.jpg", new FileInputStream("D:/upload/1.jpg"));
        log.info("文件上传完毕之后的路径{}", filePath);
    }
}

图片上传实现

后台首先使用Spring的文件上传解析器接收上传的文件,然后将文件保存到阿里云,然后返回访问地址

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

配置文件大小

在配置文件application.yml中添加下面配置

spring:
  servlet:
    multipart:
      max-request-size: 100MB # 最大请求文件大小,默认10MB
      max-file-size: 10MB # 单个请求文件大小,默认1MB

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

FileController

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系统登录

系统登录的本质是查询,即服务端接收用户发送的用户名和密码,然后跟数据库中的账号密码进行比对

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

名称说明
Entity实体对象,通常和数据表对应
DTO数据传输对象,主要用于接收前端数据,并在各层之间传递
VO视图对象,为展示前端数据提供的对象

准备DTO类

创建一个LoginDTO用于接收前端提交的登录参数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

业务代码

EmpController

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

EmpService

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

EmpServiceImpl

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

EmpMapper

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

访问测试

启动后端程序和nginx软件,使用浏览器访问http://localhost:90/#/login进入登录页面,输入账号和密码进行测试

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

登录校验

理论

我们的系统目前在不经过登录的情况下,直接输入员工页面地址就可以访问,这是非常不安全的。

正确的流程应该是:当访问请求到达服务器后,服务器要校验当前用户是否已经登录过,如果登录过,就放行请求;如果未登录过,就禁止请求访问

那如何知道用户是否已经登录过呢?

这就需要在用户登录成功后,由服务器为其颁发一个token(身份标识),然后后面用户每次发送请求,都会携带着这个token

而作为服务器会对每次的请求进行拦截,校验token的合法性即可

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

JWT

介绍

全称:JSON Web Token (https://jwt.io/),用于对应用程序上的用户进行身份标记

本质上就是一个经过加密处理与校验处理的字符串,它由三部分组成:

  • 头信息(Header):记录令牌类型和签名算法,例如:{“alg”: “HS256”,“typ”: “JWT”}
  • 有效载荷(Payload):记录一些自定义能够区分身份的非敏感信息,例如:{“id”: “1”,“username”: “tom”}
  • 签名(Signature):用于保证Token在传输过程中不被篡改,它是header、payload,加入指定算法计算得来的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用流程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

代码测试

① 在pom.xml中引入依赖

<!--Token生成与解析-->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

② 生成并校验token

package com.itheima.test;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JwtTest {

    //生成token
    @Test
    public void genJwt() {
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("username", "Tom");

        String jwt = Jwts.builder()
                .setClaims(claims) //自定义内容(载荷)
                .signWith(SignatureAlgorithm.HS256, "itheima") //签名算法和盐
                .setExpiration(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)) //有效期
                .compact();
        System.out.println(jwt);
    }

    //校验token
    @Test
    public void checkJwt() {
        Claims claims = Jwts.parser()
                .setSigningKey("itheima")//盐
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwiZXhwIjoxNjc3MzU3MjE0LCJ1c2VybmFtZSI6IlRvbSJ9.RBtRZGHUefLElDWWIlQRoy0_Dl71sZysPP61vVa46oo")//上一步得到的值
                .getBody();
        System.out.println(claims);
    }
}

功能改进

修改目前的登录功能,当登录成功后,创建token并返回给客户端

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拦截器

介绍

拦截器是Spring提供的一种技术,它会在进入controller之前,离开controller之后以及响应离开服务时进行拦截。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

入门案例

① 开发拦截器

作用:要对拦截的资源做什么

语法:实现HandlerInterceptor接口,重写3个方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

② 配置拦截器

作用:确定你要拦截那些资源

语法:在配置类中添加拦截路径的配置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

拦截路径

拦截器的路径写法相对简单,其实只有两个:*表示一层路径 **表示多层路径

路径解释备注
/*一级路径能匹配/depts,/emps,/login,不能匹配 /depts/1
/**任意级路径能匹配/depts,/depts/1,/depts/1/2
/depts/*/depts下的一级路径能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/**/depts下的任意级路径能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

拦截器链

多个拦截器也可以同时使用,一条拦截器链,他们的顺序是有.order()方法控制

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

访问校验

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 创建com.itheima.interceptor.LoginCheckInterceptor编写过滤逻辑

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

package com.itheima.interceptor;


import com.google.gson.Gson;
import com.itheima.util.JwtUtils;
import com.itheima.vo.Result;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {

    @Autowired
    private Gson gson;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1. 获取请求头中的令牌(token)
        String token = request.getHeader("token");

        //2. 解析token,如果解析失败,返回错误结果(未登录)。
        try {
            Claims claims = JwtUtils.parseJWT(token);
        }catch (Exception e){
            log.info("token错误");
            //返回错误消息
            String json = gson.toJson(Result.error("NOT_LOGIN"));
            response.setContentType("application/json;charset=utf-8");
            response.getWriter().write(json);
            return false;//结束判断
        }

        //3. 放行
        return true;
    }
}
  1. 配置拦截器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

自动装配

源码分析

SpringBootApplication注解分析

@Target({ElementType.TYPE})   //标注位置: 类  接口
@Retention(RetentionPolicy.RUNTIME) // 保留策略  运行时代码
@Documented // 生成JavaDoc
@Inherited //运行被继承

@SpringBootConfiguration // 相当于@Configuration  表示这是一个配置类
@EnableAutoConfiguration // 将所有符合自动配置条件的bean定义加载到IoC容器(重点!!!)
@ComponentScan(...) // 注解扫描  扫描的是当前类所在包及其子包中类中的注解
public @interface SpringBootApplication {}

@EnableAutoConfiguration注解分析

@AutoConfigurationPackage // 获取注解扫描的包路径
@Import({AutoConfigurationImportSelector.class})// 选择所有符合自动配置条件的bean,然后将其加载到IoC容器
public @interface EnableAutoConfiguration {}

AutoConfigurationImportSelector类分析

public class AutoConfigurationImportSelector...{
    ...

	protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
		List<String> configurations = new ArrayList<>(
				SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));
		ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).forEach(configurations::add);
		Assert.notEmpty(configurations,
				"No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you "
						+ "are using a custom packaging, make sure that file is correct.");
		return configurations;
	}
}

下面是spring-boot-autoconfigure这个jar中META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的内容

通过读取这个配置获取一组@Configuration类。

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration

org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration

GsonAutoConfiguration

@AutoConfiguration
@ConditionalOnClass(Gson.class) 
@EnableConfigurationProperties(GsonProperties.class)
public class GsonAutoConfiguration {

	...

    //向容器中放入gson对象
	@Bean
	@ConditionalOnMissingBean
	public Gson gson(GsonBuilder gsonBuilder) {
		return gsonBuilder.create();
	}
}

条件装配

* 并不是读取到的所有的配置类中Bean都会被初始化,在配置类中使用@Condition来加载满足条件的Bean
- ConditionalOnClass:      判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnProperty:   判断配置文件中是否有对应属性和值才初始化Bean 
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

源码图示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

nableConfigurationProperties(GsonProperties.class)
public class GsonAutoConfiguration {

...

//向容器中放入gson对象
@Bean
@ConditionalOnMissingBean
public Gson gson(GsonBuilder gsonBuilder) {
	return gsonBuilder.create();
}

}


>条件装配

~~~markdown
* 并不是读取到的所有的配置类中Bean都会被初始化,在配置类中使用@Condition来加载满足条件的Bean
- ConditionalOnClass:      判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnProperty:   判断配置文件中是否有对应属性和值才初始化Bean 
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean

[外链图片转存中…(img-owzGdQ00-1728558247935)]

源码图示

[外链图片转存中…(img-jYigDfYw-1728558247935)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风止￴

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值