4.1

一、后端 JWT 认证实现
在 Spring Boot 中集成 JWT,实现无状态认证:

  1. 添加依赖
    xml

    io.jsonwebtoken
    jjwt-api
    0.11.5


    io.jsonwebtoken
    jjwt-impl
    0.11.5
    runtime


    io.jsonwebtoken
    jjwt-jackson
    0.11.5
    runtime

  2. 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());
    }
    }

  3. 认证过滤器
    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);
     }
    

    }
    }

  4. 认证接口
    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 令牌并添加到请求头:

  1. 令牌管理工具类
    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 ");
    }
    }

  2. 配置 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);
         }
     }
    

    }
    }

  3. 登录与令牌管理
    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 证书,确保令牌传输过程中不被劫持

posted @ 2025-04-01 21:56  李蕊lr  阅读(17)  评论(0)    收藏  举报