- 添加依赖(pom.xml)
xml
<!-- Spring Boot 1.x 基础依赖 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.22.RELEASE</version> <!-- 兼容Java 8的版本 -->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JWT支持 -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- 如果需要数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
2. JWT工具类
java
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Component
public class JwtTokenUtil {
private static final String SECRET_KEY = "your-secret-key-change-in-production";
private static final long EXPIRATION_TIME = 86400000L; // 24小时
/**
* 生成Token
*/
public String generateToken(String username) {
Map<String, Object> claims = new HashMap<>();
claims.put("sub", username);
claims.put("created", new Date());
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
/**
* 验证Token
*/
public Boolean validateToken(String token, String username) {
final String usernameFromToken = getUsernameFromToken(token);
return (usernameFromToken.equals(username) && !isTokenExpired(token));
}
/**
* 从Token获取用户名
*/
public String getUsernameFromToken(String token) {
return getClaimsFromToken(token).getSubject();
}
/**
* 获取Token过期时间
*/
public Date getExpirationDateFromToken(String token) {
return getClaimsFromToken(token).getExpiration();
}
/**
* 解析Token
*/
private Claims getClaimsFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
/**
* 判断Token是否过期
*/
private Boolean isTokenExpired(String token) {
final Date expiration = getExpirationDateFromToken(token);
return expiration.before(new Date());
}
}
3. Token拦截器
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TokenInterceptor implements HandlerInterceptor {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
// 放行OPTIONS请求
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
return true;
}
// 获取Token
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
}
// Token验证
if (token == null || !validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"code\":401,\"message\":\"无效的Token或Token已过期\"}");
return false;
}
// 将用户名存入请求
String username = jwtTokenUtil.getUsernameFromToken(token);
request.setAttribute("username", username);
return true;
}
private boolean validateToken(String token) {
try {
String username = jwtTokenUtil.getUsernameFromToken(token);
return jwtTokenUtil.validateToken(token, username);
} catch (Exception e) {
return false;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
}
}
4. Web配置类
java
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 排除登录和注册接口
registry.addInterceptor(new TokenInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/login")
.excludePathPatterns("/api/auth/register");
}
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false)
.maxAge(3600);
}
}
5. 认证控制器
java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
/**
* 登录接口
*/
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
// 1. 验证用户名密码(这里简化处理,实际应从数据库验证)
if (!"admin".equals(loginRequest.getUsername()) ||
!"123456".equals(loginRequest.getPassword())) {
Map<String, Object> error = new HashMap<>();
error.put("code", 401);
error.put("message", "用户名或密码错误");
return ResponseEntity.status(401).body(error);
}
// 2. 生成Token
String token = jwtTokenUtil.generateToken(loginRequest.getUsername());
// 3. 返回结果
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "登录成功");
result.put("data", token);
result.put("username", loginRequest.getUsername());
return ResponseEntity.ok(result);
}
/**
* 注册接口
*/
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody RegisterRequest registerRequest) {
// 注册逻辑...
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("message", "注册成功");
return ResponseEntity.ok(result);
}
/**
* 获取用户信息(需要Token验证)
*/
@GetMapping("/userInfo")
public ResponseEntity<?> getUserInfo(@RequestAttribute String username) {
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
result.put("username", username);
result.put("roles", new String[]{"USER"});
return ResponseEntity.ok(result);
}
}
// 请求参数类
class LoginRequest {
private String username;
private String password;
// getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
}
class RegisterRequest {
private String username;
private String password;
private String email;
// getters and setters
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public String getPassword() { return password; }
public void setPassword(String password) { this.password = password; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
6. 响应封装类(可选)
java
public class ApiResponse<T> {
private int code;
private String message;
private T data;
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "成功", data);
}
public static ApiResponse<?> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
// getters and setters
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public T getData() { return data; }
public void setData(T data) { this.data = data; }
}
7. 前端使用示例
javascript
// 登录获取Token
async function login(username, password) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ username, password })
});
const result = await response.json();
if (result.code === 200) {
// 存储Token到localStorage
localStorage.setItem('token', result.data);
localStorage.setItem('username', result.username);
}
return result;
}
// 带Token的请求
async function getUserInfo() {
const token = localStorage.getItem('token');
const response = await fetch('/api/auth/userInfo', {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
});
return await response.json();
}
// 请求拦截器示例(axios)
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response.status === 401) {
// Token过期或无效,跳转到登录页
window.location.href = '/login';
}
return Promise.reject(error);
});
8. 配置文件(application.yml)
yaml
server:
port: 8080
spring:
datasource:
url: jdbc:mysql://localhost:3306/yourdb
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
jpa:
hibernate:
ddl-auto: update
show-sql: true
注意事项
-
安全性:生产环境需要将SECRET_KEY存储在安全的配置中,不要硬编码
-
Token过期:根据需求调整Token过期时间
-
刷新Token:可以添加刷新Token机制
-
HTTPS:生产环境必须使用HTTPS
-
Token存储:前端应使用HttpOnly的cookie或localStorage存储Token