最后在开发一个文件上传接口,结束后端部分开发
文件上传接口
先看接口文档
阅读接口文档,唯一问题就是项目暂时还没有传到服务器上,所以对文件的存储与读取暂时在项目本地进行
Controller层
@RestController
public class FileUploadController {
@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws IOException {
//获取文件名
String originalFilename = file.getOriginalFilename();
//保证文件的名字是唯一的,防止文件覆盖
String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//把文件内容存储到本地磁盘
file.transferTo(new File("H:\\Desktop\\chessmanfile\\"+filename));
return Result.success("rul地址");
}
}
云存储头像
初见阿里OSS
下面使用阿里云OSS服务来存储头像
有关OSS存储的获取,bucket创建等部分掠过
由于我们写的项目是一个maven项目,根据官网的SDK引导,我们需要导入如下坐标
<!-- 阿里云OSS依赖坐标-->
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
<version>3.15.1</version>
</dependency>
官网提示我们,使用1.9版本以上的JDK还需导入JAXB相关坐标
<!-- JAXB相关依赖-->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
<!-- no more than 2.3.3-->
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.3</version>
</dependency>
坐标导入完成后,我们使用官方给的一个示例文件进行测试
如上图,是从阿里云OSS官方示例文件修改而来的测试程序,其中修改了的部分都用下划线标出,第一个要修改的是endpoint,在控制台中可以找到
接下来就是accesskey的id和secret,官方文档给出的方法是将这两个信息配置进系统环境变量中去,这里不采用,直接定义两个变量来 ,再创建OSSClient实例中也作出了相应的修改
接下来就是本身的实例给服务器传入的是content,由于我们要存储的就是头像,所以直接改成传入一个图片来测试
运行了测试程序后在后台可以查看到我们的测试图片已经上传成功,下一步就是将图片的上传集成到我们的程序中去
云存储头像文件集成入程序
我们书写一个工具类来使用,实际上就是根据上述修改方法对工具类进行仿写
public class AliOSSUtil {
// Endpoint以华东1(杭州)为例,其它Region请按实际情况填写。
private static final String ENDPOINT = "https://oss-cn-beijing.aliyuncs.com";
private static final String ACCESS_KEY_ID = "马赛克";
private static final String ACCESS_KEY_SECRET = "马赛克";
// 填写Bucket名称,例如examplebucket。
private static final String BUCKET_NAME = "chessman";
public static String main(String objectName , InputStream in) throws Exception {
//初始化URL
String URL = "";
// 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(ENDPOINT,ACCESS_KEY_ID,ACCESS_KEY_SECRET );
try {
// 填写字符串。
String content = "Hello OSS,你好世界";
// 创建PutObjectRequest对象。
PutObjectRequest putObjectRequest = new PutObjectRequest(BUCKET_NAME, objectName, in);
// 如果需要上传时设置存储类型和访问权限,请参考以下示例代码。
// ObjectMetadata metadata = new ObjectMetadata();
// metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
// metadata.setObjectAcl(CannedAccessControlList.Private);
// putObjectRequest.setMetadata(metadata);
// 上传字符串。
PutObjectResult result = ossClient.putObject(putObjectRequest);
URL = "https://"+BUCKET_NAME+"."+ENDPOINT_INDEX+"/"+ObjectName;
} catch (OSSException oe) {
System.out.println("Caught an OSSException, which means your request made it to OSS, "
+ "but was rejected with an error response for some reason.");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("Caught an ClientException, which means the client encountered "
+ "a serious internal problem while trying to communicate with OSS, "
+ "such as not being able to access the network.");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
return URL;
}
}
在main方法的参数中我们传入了文件存储路径和一个字符输入流,对应的替换我们之前在测试时写死的两个地方
还有,我们之前在书写有关头像的接口时都是要获取其URL信息,所以我们要让该方法返回头像对应的URL信息
有关URL组成
https://chessman.oss-cn-beijing.aliyuncs.com/111.jpg
这是刚刚上传的111.jpg的URL,可以看出,URL的组成为
https://BucketName.Endpoint/ObjectName
这一格式
所以我们可以通过字符串的拼接来实现URL的返回
下面就是对刚刚写的文件上传接口进行修改
文件上传接口修改
@RestController
public class FileUploadController {
@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws Exception {
//获取文件名
String originalFilename = file.getOriginalFilename();
//保证文件的名字是唯一的,防止文件覆盖
String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//把文件内容存储到本地磁盘
//file.transferTo(new File("H:\\Desktop\\chessmanfile\\"+filename));
String url = AliOSSUtil.uploadFile(filename,file.getInputStream());
return Result.success(url);
}
}
至此,后端的所有接口全部构建完毕!!!!!!!!!
完结撒花!
redis登录接口优化
当然,接口书写完毕不代表程序的维护和健壮性增强也到此为止,下面就用redis对登录接口进行优化
问题提出:我们对于用户的身份验证是依靠令牌的识别,但是我们令牌只有一个24小时过期机制,当用户更新了他的密码之后,会生成一个新的令牌,但此时旧令牌没有被我们回收,仍然能使用,我们需要有令牌主动失效机制
令牌主动失效机制
登陆成功后,给浏览器相应令牌的同时,给浏览器相应令牌的同时,把该令牌存储到redis中
LoginInterceptor拦截器中,需要验证浏览器携带的令牌,并同时需要获取到redis中存储的与之相同的令牌
当用户修改密码成功后,删除redis中存储的旧令牌
SpirngBoot集成redis
导入spring-boot-starter-data-redis起步依赖
<!-- redis坐标-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在yml配置文件中,配置redis连接信息
data:
redis:
host: localhost
port: 6379
调用API(StringRedisTemplate)完成字符串的存取操作
以两个测试方法为例
@Test
public void testSet(){
//redis中存储键值对 StringRedisTemplate
ValueOperations<String, String> Operations = stringRedisTemplate.opsForValue();
Operations.set("username","cacb");
//设置键值对及其过期时间
Operations.set("timetest","haha" ,15, TimeUnit.SECONDS);
}
@Test
public void tsetGet(){
//从redis中获取键值对
ValueOperations<String, String> Operations = stringRedisTemplate.opsForValue();
System.out.println(Operations.get("username"));
}
令牌主动失效代码实现
第一步、修改登录接口代码
//将token存入到redis中
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.set(token,token,12, TimeUnit.HOURS);
return Result.success(token);
可以看到,修改就是在成功登录后向页面返回token的同时将token存入到redis中去,这里我设置的键值对过期时间与token过期时间一致
第二步、修改修改密码接口代码
userService.updatePwd( newPwd);
//删除redis中对应的token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
operations.getOperations().delete(token);
return Result.success();
可以看到,在成功玩修改了密码之后,要删除掉redis中的旧token,这里token从请求头中获取
public Result updatePwd(@RequestBody @Valid Map<String,String> params,@RequestHeader("Authorization") String token)
所以要对修改密码接口的传参进行如上修改
第三步、修该登录拦截器代码
//从redis中获取相同的的token
ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
String redis_token = operations.get(token);
if(redis_token == null){
//此时token已经失效
throw new RuntimeException();
}
可以看到,在拦截其中我们将浏览器中获取的token作为键来从redis中获取值,如果查询到的值为空,那么就证明浏览器给出的token已经失效了,所以这时候就要拦截浏览。
至此,redis完善登录相关接口工作就基本完毕了