Spring Boot + Android 实现登录功能

打印 上一主题 下一主题

主题 1674|帖子 1674|积分 5022

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
在移动互联网的今天,很多应用需要通过移动端实现与服务器的交互功能,其中登录是最常见且基础的一种功能。通过登录,用户可以获得独特的身份标识,从而访问特定的资源或服务。本篇博客将详细先容如何使用 Spring Boot 和 Android 实现一个完整的登录功能,从后端 API 的构建到 Android 端的交互,旨在为读者提供一套完整的解决方案。
1. 简单分析

在讨论如何实现登录功能之前,我们需要明确需求。通常情况下,登录功能会包含以下几个需求:


  • 用户登录:用户通过输入用户名(或手机号、邮箱)和暗码举行登录。
  • 身份验证:服务器需要验证用户身份是否合法,是否拥有访问权限。
  • Token 授权:为了制止频仍的登录操纵,服务器可以返回一个 token,客户端持有该 token 后,能够在一段时间内免去再次登录。
  • 安全性:需要防止常见的攻击本领,如暗码泄露、暴力破解等。
在本项目中,我们将采用基于 JWT(JSON Web Token) 的方式来实现无状态的登录功能,Spring Boot 作为后端框架,Android 作为前端实现登录页面及 Token 管理。

2. 项目环境设置

2.1 后端:Spring Boot 设置

首先,我们需要在后端使用 Spring Boot 作为服务端框架,选择 Spring Security 举行用户身份验证,并使用 JWT 实现无状态的登录管理。

  • 创建 Spring Boot 项目
    可以通过 Spring Initializr 快速天生项目骨架,选择如下依靠:

    • Spring Web
    • Spring Security
    • Spring Data JPA
    • MySQL(或其他数据库)
    • JWT(通过 Maven 手动引入依靠)

  • JWT 依靠引入
    在 pom.xml 文件中添加 JWT 的依靠:
    1. <dependency>
    2.     <groupId>io.jsonwebtoken</groupId>
    3.     <artifactId>jjwt-api</artifactId>
    4.     <version>0.11.2</version>
    5. </dependency>
    6. <dependency>
    7.     <groupId>io.jsonwebtoken</groupId>
    8.     <artifactId>jjwt-impl</artifactId>
    9.     <version>0.11.2</version>
    10. </dependency>
    11. <dependency>
    12.     <groupId>io.jsonwebtoken</groupId>
    13.     <artifactId>jjwt-jackson</artifactId>
    14.     <version>0.11.2</version>
    15. </dependency>
    复制代码
2.2 前端:Android 项目设置

在 Android 中,我们可以使用 Retrofit 作为网络请求库,并通过 SharedPreferences 来存储 token 信息。

  • Retrofit 依靠引入
    在 Android 项目的 build.gradle 文件中添加 Retrofit 及其相干依靠:
    1. implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    2. implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    复制代码
  • 筹划用户登录界面
    登录界面是用户举行身份验证的入口,通常包含用户名(或手机号)、暗码输入框,以及登录按钮。

3. Spring Boot 后端开辟

在这一部门,我们将重点先容后端的开辟,首先从用户模型的筹划开始,然后是 Spring Security 的设置,接着是 JWT 的集成与登录 API 的实现。
3.1 用户模型筹划

为了保存用户信息,我们首先需要筹划一个用户模型。在这里,我们使用 JPA(Java Persistence API)来定义用户实体,并将其长期化到数据库中。
  1. @Entity
  2. public class User {
  3.     @Id
  4.     @GeneratedValue(strategy = GenerationType.IDENTITY)
  5.     private Long id;
  6.     private String username;
  7.     private String password;
  8.     private String email;
  9.    
  10.     // other fields, getters and setters
  11. }
复制代码
同时,使用 UserRepository 举行数据操纵:
  1. @Repository
  2. public interface UserRepository extends JpaRepository<User, Long> {
  3.     Optional<User> findByUsername(String username);
  4. }
复制代码
3.2 Spring Security 设置

Spring Security 是 Spring 框架提供的强大的安全管理模块。在这里,我们需要对 Spring Security 举行设置,使其与 JWT 共同使用,来实现无状态的身份验证。
3.2.1 安全设置类

创建一个 SecurityConfig 类,用于设置 Spring Security:
  1. @Configuration
  2. @EnableWebSecurity
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {
  4.    
  5.     @Autowired
  6.     private JwtAuthenticationFilter jwtAuthenticationFilter;
  7.     @Override
  8.     protected void configure(HttpSecurity http) throws Exception {
  9.         http
  10.             .csrf().disable()
  11.             .authorizeRequests()
  12.             .antMatchers("/login").permitAll()
  13.             .anyRequest().authenticated()
  14.             .and()
  15.             .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
  16.     }
  17.     @Override
  18.     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  19.         auth.userDetailsService(userDetailsService())
  20.             .passwordEncoder(passwordEncoder());
  21.     }
  22.     @Bean
  23.     public PasswordEncoder passwordEncoder() {
  24.         return new BCryptPasswordEncoder();
  25.     }
  26. }
复制代码
这里我们禁用了 CSRF 掩护,因为我们将使用 JWT 举行身份验证。我们也设置了 jwtAuthenticationFilter,它将在每次请求时验证 JWT。
3.3 JWT 的集成

JWT 是一种用于在网络应用之间安全传输信息的紧凑令牌。每个 JWT 都由三部门组成:Header、Payload 和 Signature。下面,我们来实现天生和剖析 JWT 的逻辑。
3.3.1 JwtTokenUtil 工具类

创建一个 JwtTokenUtil 工具类,用于天生和验证 JWT。
  1. @Component
  2. public class JwtTokenUtil {
  3.     private static final String SECRET_KEY = "your_secret_key";
  4.     public String generateToken(UserDetails userDetails) {
  5.         return Jwts.builder()
  6.                 .setSubject(userDetails.getUsername())
  7.                 .setIssuedAt(new Date())
  8.                 .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
  9.                 .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
  10.                 .compact();
  11.     }
  12.     public String extractUsername(String token) {
  13.         return Jwts.parser()
  14.                 .setSigningKey(SECRET_KEY)
  15.                 .parseClaimsJws(token)
  16.                 .getBody()
  17.                 .getSubject();
  18.     }
  19.     public boolean validateToken(String token, UserDetails userDetails) {
  20.         final String username = extractUsername(token);
  21.         return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
  22.     }
  23.     private boolean isTokenExpired(String token) {
  24.         final Date expiration = Jwts.parser()
  25.                 .setSigningKey(SECRET_KEY)
  26.                 .parseClaimsJws(token)
  27.                 .getBody()
  28.                 .getExpiration();
  29.         return expiration.before(new Date());
  30.     }
  31. }
复制代码
3.3.2 JwtAuthenticationFilter

JwtAuthenticationFilter 用于拦截请求并验证 token,确保只有颠末身份验证的用户可以访问受掩护的资源。
  1. @Component
  2. public class JwtAuthenticationFilter extends OncePerRequestFilter {
  3.     @Autowired
  4.     private JwtTokenUtil jwtTokenUtil;
  5.     @Autowired
  6.     private UserDetailsService userDetailsService;
  7.     @Override
  8.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
  9.             throws ServletException, IOException {
  10.         final String authorizationHeader = request.getHeader("Authorization");
  11.         String username = null;
  12.         String jwt = null;
  13.         if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
  14.             jwt = authorizationHeader.substring(7);
  15.             username = jwtTokenUtil.extractUsername(jwt);
  16.         }
  17.         if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
  18.             UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
  19.             if (jwtTokenUtil.validateToken(jwt, userDetails)) {
  20.                 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
  21.                         userDetails, null, userDetails.getAuthorities());
  22.                 authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
  23.                 SecurityContextHolder.getContext().setAuthentication(authenticationToken);
  24.             }
  25.         }
  26.         filterChain.doFilter(request, response);
  27.     }
  28. }
复制代码
3.4 登录 API 实现

在服务器端,我们需要提供一个登录的 API,用户通过该 API 发送用户名和暗码,服务器验证后天生 JWT 返回给客户端。
  1. @RestController
  2. public class AuthController {
  3.     @Autowired
  4.     private AuthenticationManager authenticationManager;
  5.     @Autowired
  6.     private JwtTokenUtil jwtTokenUtil;
  7.     @Autowired
  8.     private UserDetailsService userDetailsService;
  9.     @PostMapping("/login")
  10.     public ResponseEntity<?> createAuthenticationToken
  11. (@RequestBody AuthRequest authRequest) throws Exception {
  12.         try {
  13.             authenticationManager.authenticate(
  14.                     new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
  15.             );
  16.         } catch (BadCredentialsException e) {
  17.             throw new Exception("Incorrect username or password", e);
  18.         }
  19.         final UserDetails userDetails = userDetailsService
  20.                 .loadUserByUsername(authRequest.getUsername());
  21.         final String jwt = jwtTokenUtil.generateToken(userDetails);
  22.         return ResponseEntity.ok(new AuthResponse(jwt));
  23.     }
  24. }
复制代码
这里,AuthRequest 是用户登录时发送的请求对象,包含用户名和暗码。而 AuthResponse 是服务器返回的相应对象,包含天生的 JWT。

4. Android 前端开辟

接下来,我们将在 Android 中实现登录页面,并与 Spring Boot 后端举行交互。
4.1 使用 Retrofit 举行网络请求

Retrofit 是 Android 平台上广泛使用的网络请求库。首先,我们定义一个接口用于请求登录 API。
  1. public interface ApiService {
  2.     @POST("login")
  3.     Call<AuthResponse> login(@Body AuthRequest authRequest);
  4. }
复制代码
AuthRequest 类对应后端的登录请求体,AuthResponse 类则用来接收服务器返回的 JWT。
  1. public class AuthRequest {
  2.     private String username;
  3.     private String password;
  4.     // Constructor, getters and setters
  5. }
  6. public class AuthResponse {
  7.     private String jwt;
  8.     // Constructor, getters and setters
  9. }
复制代码
4.2 登录页面筹划与实现

接下来,我们筹划一个简单的登录界面,包括两个 EditText 组件用于输入用户名和暗码,外加一个 Button 举行登录操纵。
  1. <EditText
  2.     android:id="@+id/etUsername"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="wrap_content"
  5.     android:hint="Username" />
  6. <EditText
  7.     android:id="@+id/etPassword"
  8.     android:layout_width="match_parent"
  9.     android:layout_height="wrap_content"
  10.     android:hint="Password"
  11.     android:inputType="textPassword" />
  12. <Button
  13.     android:id="@+id/btnLogin"
  14.     android:layout_width="wrap_content"
  15.     android:layout_height="wrap_content"
  16.     android:text="Login" />
复制代码
在 MainActivity.java 中实现登录逻辑:
  1. public class MainActivity extends AppCompatActivity {
  2.     private EditText etUsername;
  3.     private EditText etPassword;
  4.     private Button btnLogin;
  5.     private ApiService apiService;
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(R.layout.activity_main);
  10.         etUsername = findViewById(R.id.etUsername);
  11.         etPassword = findViewById(R.id.etPassword);
  12.         btnLogin = findViewById(R.id.btnLogin);
  13.         Retrofit retrofit = new Retrofit.Builder()
  14.                 .baseUrl("http://your-server-url/")
  15.                 .addConverterFactory(GsonConverterFactory.create())
  16.                 .build();
  17.         apiService = retrofit.create(ApiService.class);
  18.         btnLogin.setOnClickListener(v -> login());
  19.     }
  20.     private void login() {
  21.         String username = etUsername.getText().toString();
  22.         String password = etPassword.getText().toString();
  23.         AuthRequest authRequest = new AuthRequest(username, password);
  24.         Call<AuthResponse> call = apiService.login(authRequest);
  25.         call.enqueue(new Callback<AuthResponse>() {
  26.             @Override
  27.             public void onResponse(Call<AuthResponse> call, Response<AuthResponse> response) {
  28.                 if (response.isSuccessful()) {
  29.                     String jwt = response.body().getJwt();
  30.                     // Store JWT in SharedPreferences
  31.                     SharedPreferences preferences = getSharedPreferences("my_prefs", MODE_PRIVATE);
  32.                     preferences.edit().putString("jwt", jwt).apply();
  33.                     // Navigate to another activity
  34.                 } else {
  35.                     // Handle failure
  36.                 }
  37.             }
  38.             @Override
  39.             public void onFailure(Call<AuthResponse> call, Throwable t) {
  40.                 // Handle network failure
  41.             }
  42.         });
  43.     }
  44. }
复制代码
4.3 Token 的存储和管理

为了在后续的请求中使用 JWT,我们可以将其存储在 Android 的 SharedPreferences 中。这样,用户登录后,应用在关闭再打开时依然可以保持登录状态。
  1.             (Call<AuthResponse> call, Response<AuthResponse> response) {
  2.                 if (response.isSuccessful()) {
  3.                     AuthResponse authResponse = response.body();
  4.                     String token = authResponse.getJwt();
  5.                     
  6.                     // Store the token using SharedPreferences
  7.                     SharedPreferences sharedPreferences = getSharedPreferences("MyApp", MODE_PRIVATE);
  8.                     SharedPreferences.Editor editor = sharedPreferences.edit();
  9.                     editor.putString("JWT_TOKEN", token);
  10.                     editor.apply();
  11.                     
  12.                     Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
  13.                     
  14.                     // Navigate to another activity after successful login
  15.                     Intent intent = new Intent(MainActivity.this, DashboardActivity.class);
  16.                     startActivity(intent);
  17.                     finish();
  18.                 } else {
  19.                     Toast.makeText(MainActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
  20.                 }
  21.             }
  22.             @Override
  23.             public void onFailure(Call<AuthResponse> call, Throwable t) {
  24.                 Toast.makeText(MainActivity.this, "Network error", Toast.LENGTH_SHORT).show();
  25.             }
  26.         });
  27.     }
  28. }
复制代码
在上面的代码中,login() 方法负责发送登录请求并处置惩罚服务器的相应。假如登录成功,我们将获取到服务器返回的 JWT 并将其存储在 SharedPreferences 中,以便在后续的请求中使用该 Token 举行身份验证。
4.3 Token 的存储和管理

为了保证用户登录后的身份验证,客户端需要将服务器返回的 JWT 存储起来。SharedPreferences 是 Android 中一种轻量级的数据存储方式,非常适合保存类似于 Token 这样的设置信息。
  1. SharedPreferences sharedPreferences = getSharedPreferences("MyApp", MODE_PRIVATE);
  2. String token = sharedPreferences.getString("JWT_TOKEN", null);
复制代码
在需要身份验证的请求中,我们可以从 SharedPreferences 中读取保存的 Token,并在请求头中添加该 Token。
  1. OkHttpClient client = new OkHttpClient.Builder()
  2.     .addInterceptor(chain -> {
  3.         Request originalRequest = chain.request();
  4.         String token = sharedPreferences.getString("JWT_TOKEN", null);
  5.         if (token != null) {
  6.             Request newRequest = originalRequest.newBuilder()
  7.                     .header("Authorization", "Bearer " + token)
  8.                     .build();
  9.             return chain.proceed(newRequest);
  10.         }
  11.         return chain.proceed(originalRequest);
  12.     })
  13.     .build();
复制代码
通过上述代码,所有发送的请求将携带 JWT,服务端能够通过验证 Token 来判断用户是否具有访问权限。

5. 完整登录流程分析


  • 用户在 Android 客户端输入用户名和暗码,点击登录按钮。
  • 客户端发送 POST 请求到服务器的 /login 接口,请求体中包含用户名和暗码。
  • 服务器验证用户的身份,假如验证成功,则天生 JWT 并返回给客户端。
  • 客户端接收到 JWT 后,将其存储在 SharedPreferences 中。
  • 后续请求时,客户端将 JWT 附加在请求头中,服务器根据 JWT 来判断用户是否有权限访问资源。

6. 安全性及优化计谋

6.1 HTTPS 加密传输

为了确保数据传输的安全性,发起在实际项目中使用 HTTPS 举行加密传输,制止用户的敏感信息(如暗码)被偷取。
6.2 暗码加密存储

在服务器端,用户的暗码不应该以明文形式存储。通常,我们会使用 BCrypt 等加密算法对用户暗码举行加密后再存储到数据库中。
  1. @Bean
  2. public PasswordEncoder passwordEncoder() {
  3.     return new BCryptPasswordEncoder();
  4. }
复制代码
6.3 Token 的过期管理

JWT 通常会设置一个过期时间,以确保 Token 不会被长期滥用。客户端在检测到 Token 过期时,应提示用户重新登录。
6.4 防止暴力破解

为了防止恶意用户通过暴力破解获取用户暗码,发起在登录接口上增长防护机制,如使用验证码,或在多次登录失败后暂时锁定用户账号。

7. 总结

本篇博客先容了如何使用 Spring Boot 和 Android 实现一个完整的登录功能。从用户模型的筹划、Spring Security 的设置、JWT 的集成,到 Android 客户端的登录页面实现、网络请求和 Token 管理,涵盖了从后端到前端的所有关键步骤。登录功能虽然看似简单,但其背后涉及的安全性和可扩展性都是我们需要重点关注的。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万有斥力

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表