一站式网上办事大厅
小明:嘿,李工,我最近在做一个“一站式网上服务大厅”的项目,想实现一个“下载”功能,但不知道怎么开始,你能帮我看看吗?
李工:当然可以!首先,你需要明确“一站式服务大厅”的核心目标是为用户提供统一的访问入口,而“下载”功能则是其中的一个重要模块。你打算用什么技术来实现呢?比如前端用React,后端用Node.js或Spring Boot?
小明:我们准备用Spring Boot做后端,前端用Vue.js。不过,用户需要先登录才能下载文件,这该怎么处理呢?
李工:好问题。登录是关键,因为它决定了谁可以访问哪些资源。我们需要在用户登录后生成一个令牌(Token),比如JWT,然后在下载请求时验证这个令牌是否有效。
小明:那具体怎么实现呢?能给我一个例子吗?
李工:当然可以。我们先从登录接口开始写起,用户提交用户名和密码,服务器验证成功后返回一个JWT Token。然后在下载文件时,前端将Token放在请求头中,后端校验Token是否合法。

小明:听起来不错。那具体的代码结构是怎样的?
李工:我们可以分几个部分来看。首先是登录接口的实现,接着是下载接口的逻辑,最后是安全配置。
小明:好的,那我先看看登录接口的代码吧。
李工:这是Spring Boot中一个简单的登录接口示例:
@RestController
public class AuthController {
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest request) {
// 假设这里进行用户名和密码的验证
if ("admin".equals(request.getUsername()) && "123456".equals(request.getPassword())) {
String token = JwtUtil.generateToken("admin");
return ResponseEntity.ok(token);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
}
class LoginRequest {
private String username;
private String password;
// getters and setters
}
小明:这段代码看起来很清晰。那JWT工具类是怎么实现的?
李工:这是一个简单的JWT生成和解析工具类,使用了JWTCreator库:
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public static String generateToken(String username) {
return JWT.create()
.withSubject(username)
.withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.sign(Algorithm.HMAC256(SECRET_KEY));
}
public static String getUsernameFromToken(String token) {
return JWT.decode(token).getSubject();
}
}
小明:明白了。那下载接口怎么实现呢?
李工:下载接口需要检查用户是否有权限下载该文件。我们可以在方法上添加@PreAuthorize注解,或者手动校验Token。
小明:我想用手动校验的方式,这样更灵活。
李工:好的,下面是一个简单的下载接口示例:
@RestController
public class FileController {
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename, @RequestHeader("Authorization") String token) {
try {
String username = JwtUtil.getUsernameFromToken(token);
// 这里可以添加权限校验逻辑,比如检查用户是否有权限下载该文件
Resource file = new FileSystemResource("path/to/files/" + filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"")
.body(file);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
}
小明:这个接口看起来没问题,但用户怎么获取Token呢?是不是必须先登录?
李工:没错,用户必须先调用登录接口获取Token,然后在后续请求中带上这个Token。例如,在下载文件前,前端应该先发送登录请求,并保存返回的Token。
小明:明白了。那如果用户没有登录就尝试下载,会发生什么?
李工:如果用户没有提供Token,或者Token无效,下载接口会返回403 Forbidden错误,提示用户没有权限。
小明:那在前端,怎么处理这些情况呢?比如,如果用户没登录,直接跳转到登录页面?
李工:是的,通常我们会拦截所有需要登录的请求,如果发现Token无效或缺失,就跳转到登录页面。或者在前端使用axios拦截器,自动处理Token过期的情况。
小明:那我可以使用Vue Router的导航守卫来实现吗?
李工:当然可以。你可以使用beforeEach钩子来检查每个路由是否需要登录,如果没有Token,就跳转到登录页。
小明:那前端代码应该怎么写呢?
李工:下面是一个简单的Vue Router导航守卫示例:
router.beforeEach((to, from, next) => {
const token = localStorage.getItem('token');
if (to.meta.requiresAuth && !token) {
next({ path: '/login' });
} else {
next();
}
});
小明:明白了。那登录页面怎么设计?
李工:登录页面一般包括用户名、密码输入框和登录按钮。点击登录后,调用后端的/login接口,拿到Token后保存到localStorage或sessionStorage中。
小明:那登录成功后,怎么跳转到首页或者之前访问的页面?
李工:通常我们会把用户跳转到登录前的页面,或者设置一个默认跳转路径。例如,在登录成功后,根据from.path跳转回原页面,或者直接跳转到主页。
小明:明白了。那整个流程大致是:用户登录 -> 获取Token -> 使用Token访问受保护的资源(如下载)。
李工:没错。现在,你已经掌握了基本的登录和下载功能的实现方式。接下来,你可以考虑加入更多安全机制,比如刷新Token、防止Token泄露等。
小明:谢谢李工,我现在对这个项目更有信心了!
李工:不客气,有问题随时问我!祝你项目顺利!