4.1
一、后端 JWT 认证实现
在 Spring Boot 中集成 JWT,实现无状态认证:
-
添加依赖
xml
io.jsonwebtoken
jjwt-api
0.11.5
io.jsonwebtoken
jjwt-impl
0.11.5
runtime
io.jsonwebtoken
jjwt-jackson
0.11.5
runtime
-
JWT 工具类
java
public class JwtTokenUtil {
private static final String SECRET_KEY = "demoapp_secret_key_2023_springboot_android";
private static final long EXPIRATION_TIME = 86400000; // 24小时(毫秒)// 生成JWT令牌
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}// 验证令牌有效性并获取用户名
public String validateToken(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
} catch (Exception e) {
return null;
}
}// 从令牌中获取过期时间
public Date getExpirationDate(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
return claims.getExpiration();
}// 检查令牌是否过期
public boolean isTokenExpired(String token) {
Date expiration = getExpirationDate(token);
return expiration.before(new Date());
}
} -
认证过滤器
java
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtTokenUtil jwtTokenUtil;@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll() // 认证接口放行
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenUtil), UsernamePasswordAuthenticationFilter.class);
}// JWT认证过滤器
private static class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtTokenUtil jwtTokenUtil;public JwtAuthenticationFilter(JwtTokenUtil jwtTokenUtil) { this.jwtTokenUtil = jwtTokenUtil; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authHeader != null && authHeader.startsWith("Bearer ")) { jwt = authHeader.substring(7); try { username = jwtTokenUtil.validateToken(jwt); } catch (Exception e) { logger.error("JWT验证失败", e); } } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(auth); } } chain.doFilter(request, response); }}
} -
认证接口
java
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;@PostMapping("/login")
public ResponseEntity<Map<String, String>> login(@RequestBody LoginRequest request) {
// 验证用户名密码(实际项目中应使用BCrypt加密比对)
UserDetails userDetails = userDetailsService.loadUserByUsername(request.getUsername());
if (!userDetails.getPassword().equals(request.getPassword())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(Collections.singletonMap("error", "用户名或密码错误"));
}// 生成JWT令牌 String token = jwtTokenUtil.generateToken(request.getUsername()); return ResponseEntity.ok(Collections.singletonMap("token", token));}
}
二、Android 端 JWT 集成
在 Android 中管理 JWT 令牌并添加到请求头:
-
令牌管理工具类
java
public class JwtManager {
private static final String TOKEN_KEY = "jwt_token";
private static JwtManager instance;
private SharedPreferences preferences;private JwtManager() {
preferences = ContextProvider.getContext().getSharedPreferences(
"demo_app_preferences", Context.MODE_PRIVATE);
}public static JwtManager getInstance() {
if (instance == null) {
instance = new JwtManager();
}
return instance;
}// 保存令牌
public void saveToken(String token) {
preferences.edit().putString(TOKEN_KEY, token).apply();
}// 获取令牌
public String getToken() {
return preferences.getString(TOKEN_KEY, "");
}// 清除令牌
public void clearToken() {
preferences.edit().remove(TOKEN_KEY).apply();
}// 检查令牌是否有效(简化判断,实际应解析JWT过期时间)
public boolean isTokenValid() {
String token = getToken();
return !TextUtils.isEmpty(token) && !token.startsWith("Bearer ");
}
} -
配置 OkHttp 添加令牌到请求头
java
public class RetrofitClient {
private static final String BASE_URL = "http://192.168.1.100:8080/";public static ApiService getApiService() {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.addInterceptor(new JwtInterceptor()) // 添加JWT拦截器
.build();Retrofit retrofit = new Retrofit.Builder() .baseUrl(BASE_URL) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build(); return retrofit.create(ApiService.class);}
// JWT拦截器
private static class JwtInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request originalRequest = chain.request();
String token = JwtManager.getInstance().getToken();if (!TextUtils.isEmpty(token)) { Request newRequest = originalRequest.newBuilder() .header("Authorization", "Bearer " + token) .build(); return chain.proceed(newRequest); } else { return chain.proceed(originalRequest); } }}
} -
登录与令牌管理
java
// 登录界面逻辑
apiService.login(username, password).enqueue(new Callback<ApiResponse>() {
@Override
public void onResponse(Call<ApiResponse> call, Response<ApiResponse > response) {
if (response.isSuccessful() && response.body() != null) {
String token = response.body().getData();
JwtManager.getInstance().saveToken(token);
Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
// 跳转到主页
} else {
Toast.makeText(LoginActivity.this, "登录失败", Toast.LENGTH_SHORT).show();
}
}
});
// 退出登录
JwtManager.getInstance().clearToken();
三、安全增强建议
令牌刷新机制:
当 JWT 过期时,后端返回 401 错误,Android 端使用 Refresh Token 获取新令牌
设备绑定:
在 JWT 中添加设备指纹,防止令牌在其他设备使用
HTTPS 加密:
部署 SSL 证书,确保令牌传输过程中不被劫持

浙公网安备 33010602011771号