马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
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 的依靠:
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-api</artifactId>
- <version>0.11.2</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-impl</artifactId>
- <version>0.11.2</version>
- </dependency>
- <dependency>
- <groupId>io.jsonwebtoken</groupId>
- <artifactId>jjwt-jackson</artifactId>
- <version>0.11.2</version>
- </dependency>
复制代码 2.2 前端:Android 项目设置
在 Android 中,我们可以使用 Retrofit 作为网络请求库,并通过 SharedPreferences 来存储 token 信息。
- Retrofit 依靠引入
在 Android 项目的 build.gradle 文件中添加 Retrofit 及其相干依靠:
- implementation 'com.squareup.retrofit2:retrofit:2.9.0'
- implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
复制代码 - 筹划用户登录界面
登录界面是用户举行身份验证的入口,通常包含用户名(或手机号)、暗码输入框,以及登录按钮。
3. Spring Boot 后端开辟
在这一部门,我们将重点先容后端的开辟,首先从用户模型的筹划开始,然后是 Spring Security 的设置,接着是 JWT 的集成与登录 API 的实现。
3.1 用户模型筹划
为了保存用户信息,我们首先需要筹划一个用户模型。在这里,我们使用 JPA(Java Persistence API)来定义用户实体,并将其长期化到数据库中。
- @Entity
- public class User {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private Long id;
- private String username;
- private String password;
- private String email;
-
- // other fields, getters and setters
- }
复制代码 同时,使用 UserRepository 举行数据操纵:
- @Repository
- public interface UserRepository extends JpaRepository<User, Long> {
- Optional<User> findByUsername(String username);
- }
复制代码 3.2 Spring Security 设置
Spring Security 是 Spring 框架提供的强大的安全管理模块。在这里,我们需要对 Spring Security 举行设置,使其与 JWT 共同使用,来实现无状态的身份验证。
3.2.1 安全设置类
创建一个 SecurityConfig 类,用于设置 Spring Security:
- @Configuration
- @EnableWebSecurity
- public class SecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Autowired
- private JwtAuthenticationFilter jwtAuthenticationFilter;
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- http
- .csrf().disable()
- .authorizeRequests()
- .antMatchers("/login").permitAll()
- .anyRequest().authenticated()
- .and()
- .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
- }
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(userDetailsService())
- .passwordEncoder(passwordEncoder());
- }
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
- }
复制代码 这里我们禁用了 CSRF 掩护,因为我们将使用 JWT 举行身份验证。我们也设置了 jwtAuthenticationFilter,它将在每次请求时验证 JWT。
3.3 JWT 的集成
JWT 是一种用于在网络应用之间安全传输信息的紧凑令牌。每个 JWT 都由三部门组成:Header、Payload 和 Signature。下面,我们来实现天生和剖析 JWT 的逻辑。
3.3.1 JwtTokenUtil 工具类
创建一个 JwtTokenUtil 工具类,用于天生和验证 JWT。
- @Component
- public class JwtTokenUtil {
- private static final String SECRET_KEY = "your_secret_key";
- public String generateToken(UserDetails userDetails) {
- return Jwts.builder()
- .setSubject(userDetails.getUsername())
- .setIssuedAt(new Date())
- .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
- .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
- .compact();
- }
- public String extractUsername(String token) {
- return Jwts.parser()
- .setSigningKey(SECRET_KEY)
- .parseClaimsJws(token)
- .getBody()
- .getSubject();
- }
- public boolean validateToken(String token, UserDetails userDetails) {
- final String username = extractUsername(token);
- return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
- }
- private boolean isTokenExpired(String token) {
- final Date expiration = Jwts.parser()
- .setSigningKey(SECRET_KEY)
- .parseClaimsJws(token)
- .getBody()
- .getExpiration();
- return expiration.before(new Date());
- }
- }
复制代码 3.3.2 JwtAuthenticationFilter
JwtAuthenticationFilter 用于拦截请求并验证 token,确保只有颠末身份验证的用户可以访问受掩护的资源。
- @Component
- public class JwtAuthenticationFilter extends OncePerRequestFilter {
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
- @Autowired
- private UserDetailsService userDetailsService;
- @Override
- protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
- throws ServletException, IOException {
- final String authorizationHeader = request.getHeader("Authorization");
- String username = null;
- String jwt = null;
- if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
- jwt = authorizationHeader.substring(7);
- username = jwtTokenUtil.extractUsername(jwt);
- }
- if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
- UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
- if (jwtTokenUtil.validateToken(jwt, userDetails)) {
- UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
- userDetails, null, userDetails.getAuthorities());
- authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
- SecurityContextHolder.getContext().setAuthentication(authenticationToken);
- }
- }
- filterChain.doFilter(request, response);
- }
- }
复制代码 3.4 登录 API 实现
在服务器端,我们需要提供一个登录的 API,用户通过该 API 发送用户名和暗码,服务器验证后天生 JWT 返回给客户端。
- @RestController
- public class AuthController {
- @Autowired
- private AuthenticationManager authenticationManager;
- @Autowired
- private JwtTokenUtil jwtTokenUtil;
- @Autowired
- private UserDetailsService userDetailsService;
- @PostMapping("/login")
- public ResponseEntity<?> createAuthenticationToken
- (@RequestBody AuthRequest authRequest) throws Exception {
- try {
- authenticationManager.authenticate(
- new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
- );
- } catch (BadCredentialsException e) {
- throw new Exception("Incorrect username or password", e);
- }
- final UserDetails userDetails = userDetailsService
- .loadUserByUsername(authRequest.getUsername());
- final String jwt = jwtTokenUtil.generateToken(userDetails);
- return ResponseEntity.ok(new AuthResponse(jwt));
- }
- }
复制代码 这里,AuthRequest 是用户登录时发送的请求对象,包含用户名和暗码。而 AuthResponse 是服务器返回的相应对象,包含天生的 JWT。
4. Android 前端开辟
接下来,我们将在 Android 中实现登录页面,并与 Spring Boot 后端举行交互。
4.1 使用 Retrofit 举行网络请求
Retrofit 是 Android 平台上广泛使用的网络请求库。首先,我们定义一个接口用于请求登录 API。
- public interface ApiService {
- @POST("login")
- Call<AuthResponse> login(@Body AuthRequest authRequest);
- }
复制代码 AuthRequest 类对应后端的登录请求体,AuthResponse 类则用来接收服务器返回的 JWT。
- public class AuthRequest {
- private String username;
- private String password;
- // Constructor, getters and setters
- }
- public class AuthResponse {
- private String jwt;
- // Constructor, getters and setters
- }
复制代码 4.2 登录页面筹划与实现
接下来,我们筹划一个简单的登录界面,包括两个 EditText 组件用于输入用户名和暗码,外加一个 Button 举行登录操纵。
- <EditText
- android:id="@+id/etUsername"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="Username" />
- <EditText
- android:id="@+id/etPassword"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:hint="Password"
- android:inputType="textPassword" />
- <Button
- android:id="@+id/btnLogin"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="Login" />
复制代码 在 MainActivity.java 中实现登录逻辑:
- public class MainActivity extends AppCompatActivity {
- private EditText etUsername;
- private EditText etPassword;
- private Button btnLogin;
- private ApiService apiService;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- etUsername = findViewById(R.id.etUsername);
- etPassword = findViewById(R.id.etPassword);
- btnLogin = findViewById(R.id.btnLogin);
- Retrofit retrofit = new Retrofit.Builder()
- .baseUrl("http://your-server-url/")
- .addConverterFactory(GsonConverterFactory.create())
- .build();
- apiService = retrofit.create(ApiService.class);
- btnLogin.setOnClickListener(v -> login());
- }
- private void login() {
- String username = etUsername.getText().toString();
- String password = etPassword.getText().toString();
- AuthRequest authRequest = new AuthRequest(username, password);
- Call<AuthResponse> call = apiService.login(authRequest);
- call.enqueue(new Callback<AuthResponse>() {
- @Override
- public void onResponse(Call<AuthResponse> call, Response<AuthResponse> response) {
- if (response.isSuccessful()) {
- String jwt = response.body().getJwt();
- // Store JWT in SharedPreferences
- SharedPreferences preferences = getSharedPreferences("my_prefs", MODE_PRIVATE);
- preferences.edit().putString("jwt", jwt).apply();
- // Navigate to another activity
- } else {
- // Handle failure
- }
- }
- @Override
- public void onFailure(Call<AuthResponse> call, Throwable t) {
- // Handle network failure
- }
- });
- }
- }
复制代码 4.3 Token 的存储和管理
为了在后续的请求中使用 JWT,我们可以将其存储在 Android 的 SharedPreferences 中。这样,用户登录后,应用在关闭再打开时依然可以保持登录状态。
- (Call<AuthResponse> call, Response<AuthResponse> response) {
- if (response.isSuccessful()) {
- AuthResponse authResponse = response.body();
- String token = authResponse.getJwt();
-
- // Store the token using SharedPreferences
- SharedPreferences sharedPreferences = getSharedPreferences("MyApp", MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPreferences.edit();
- editor.putString("JWT_TOKEN", token);
- editor.apply();
-
- Toast.makeText(MainActivity.this, "Login successful", Toast.LENGTH_SHORT).show();
-
- // Navigate to another activity after successful login
- Intent intent = new Intent(MainActivity.this, DashboardActivity.class);
- startActivity(intent);
- finish();
- } else {
- Toast.makeText(MainActivity.this, "Login failed", Toast.LENGTH_SHORT).show();
- }
- }
- @Override
- public void onFailure(Call<AuthResponse> call, Throwable t) {
- Toast.makeText(MainActivity.this, "Network error", Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
复制代码 在上面的代码中,login() 方法负责发送登录请求并处置惩罚服务器的相应。假如登录成功,我们将获取到服务器返回的 JWT 并将其存储在 SharedPreferences 中,以便在后续的请求中使用该 Token 举行身份验证。
4.3 Token 的存储和管理
为了保证用户登录后的身份验证,客户端需要将服务器返回的 JWT 存储起来。SharedPreferences 是 Android 中一种轻量级的数据存储方式,非常适合保存类似于 Token 这样的设置信息。
- SharedPreferences sharedPreferences = getSharedPreferences("MyApp", MODE_PRIVATE);
- String token = sharedPreferences.getString("JWT_TOKEN", null);
复制代码 在需要身份验证的请求中,我们可以从 SharedPreferences 中读取保存的 Token,并在请求头中添加该 Token。
- OkHttpClient client = new OkHttpClient.Builder()
- .addInterceptor(chain -> {
- Request originalRequest = chain.request();
- String token = sharedPreferences.getString("JWT_TOKEN", null);
- if (token != null) {
- Request newRequest = originalRequest.newBuilder()
- .header("Authorization", "Bearer " + token)
- .build();
- return chain.proceed(newRequest);
- }
- return chain.proceed(originalRequest);
- })
- .build();
复制代码 通过上述代码,所有发送的请求将携带 JWT,服务端能够通过验证 Token 来判断用户是否具有访问权限。
5. 完整登录流程分析
- 用户在 Android 客户端输入用户名和暗码,点击登录按钮。
- 客户端发送 POST 请求到服务器的 /login 接口,请求体中包含用户名和暗码。
- 服务器验证用户的身份,假如验证成功,则天生 JWT 并返回给客户端。
- 客户端接收到 JWT 后,将其存储在 SharedPreferences 中。
- 后续请求时,客户端将 JWT 附加在请求头中,服务器根据 JWT 来判断用户是否有权限访问资源。
6. 安全性及优化计谋
6.1 HTTPS 加密传输
为了确保数据传输的安全性,发起在实际项目中使用 HTTPS 举行加密传输,制止用户的敏感信息(如暗码)被偷取。
6.2 暗码加密存储
在服务器端,用户的暗码不应该以明文形式存储。通常,我们会使用 BCrypt 等加密算法对用户暗码举行加密后再存储到数据库中。
- @Bean
- public PasswordEncoder passwordEncoder() {
- return new BCryptPasswordEncoder();
- }
复制代码 6.3 Token 的过期管理
JWT 通常会设置一个过期时间,以确保 Token 不会被长期滥用。客户端在检测到 Token 过期时,应提示用户重新登录。
6.4 防止暴力破解
为了防止恶意用户通过暴力破解获取用户暗码,发起在登录接口上增长防护机制,如使用验证码,或在多次登录失败后暂时锁定用户账号。
7. 总结
本篇博客先容了如何使用 Spring Boot 和 Android 实现一个完整的登录功能。从用户模型的筹划、Spring Security 的设置、JWT 的集成,到 Android 客户端的登录页面实现、网络请求和 Token 管理,涵盖了从后端到前端的所有关键步骤。登录功能虽然看似简单,但其背后涉及的安全性和可扩展性都是我们需要重点关注的。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |