基于Spring Boot的农业大学教材征订信息管理系统登录模块设计与实现
小明:嘿,老王,最近我们学校要开发一个教材征订信息管理系统,我负责的是登录模块,你对这个有什么建议吗?
老王:嗯,登录模块是系统的核心部分之一,必须确保安全性和稳定性。你们用的是什么技术栈?
小明:我们打算用Spring Boot来搭建后端,前端用Vue.js,数据库用MySQL。你觉得这样合适吗?
老王:挺不错的组合,Spring Boot适合快速开发,Vue也方便前后端分离。不过登录模块需要考虑很多细节,比如密码加密、令牌机制、防止SQL注入等等。
小明:对,密码加密我们打算用BCrypt,但是令牌机制具体怎么实现呢?
老王:可以使用JWT(JSON Web Token),它是一种无状态的认证方式,非常适合微服务架构。你可以生成一个token,返回给客户端,之后每次请求都带上这个token。
小明:明白了。那在Spring Boot中怎么集成JWT呢?有没有现成的库可以用?
老王:有的,可以使用jjwt这个库。首先你需要创建一个JWT工具类,用于生成和解析token。然后在登录接口中验证用户身份,成功后生成token并返回。
小明:好的,那我可以写一个LoginController来处理登录请求,然后调用AuthService进行用户验证。
老王:没错,接下来你要考虑如何将token加入到请求头中,比如Authorization字段,格式为Bearer + token。
小明:那在拦截器里如何校验token呢?
老王:可以通过Spring的拦截器或者过滤器,在请求到达控制器之前检查token是否有效。如果无效,直接返回401未授权。
小明:明白了。那关于用户表的设计,应该包含哪些字段?
老王:通常包括用户名、密码、角色等信息。密码要用BCrypt加密存储,角色用来控制权限。
小明:那我们还需要一个UserDetailsService来加载用户信息,对吧?
老王:对,Spring Security提供了UserDetailsService接口,你可以自定义实现,从数据库中查询用户信息,并返回一个UserDetails对象。
小明:那在登录时,如何验证用户输入的密码是否正确呢?
老王:Spring Security会自动处理密码的比对,只要你在UserDetailsService中返回的UserDetails对象的password字段是加密后的值,它就会自动验证。
小明:明白了。那我们可以开始编写代码了。先从配置Spring Security开始。
老王:对,首先在application.properties中配置数据库连接,然后添加Spring Security依赖。
小明:好的,我来写一下pom.xml中的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
老王:这些依赖已经足够了。接下来我们创建一个SecurityConfig类,配置Spring Security。
小明:好的,我来写这个类:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
老王:这个配置禁用了CSRF保护,因为JWT是无状态的,不需要会话管理。然后设置所有/auth路径下的请求不需要认证,其他请求都需要认证。
小明:那接下来我们要写JWT的工具类了。
老王:对,可以创建一个JwtUtil类,用于生成和解析token。
小明:好的,我来写这个类:
@Component
public class JwtUtil {
private String secretKey = "your-secret-key";
private long expiration = 86400000; // 24小时
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(SignatureAlgorithm.HS512, secretKey)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean isTokenExpired(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody()
.getExpiration().before(new Date());
}
}
老王:这个类实现了生成token、解析token和判断token是否过期的功能。
小明:那我们需要一个拦截器来检查token是否有效。
老王:对,创建一个JwtAuthenticationFilter类,继承OncePerRequestFilter。
小明:好的,我来写这个类:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
String username = jwtUtil.getUsernameFromToken(token);
if (username != null && !jwtUtil.isTokenExpired(token)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
username, null, new ArrayList<>());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
老王:这个过滤器会在每个请求到来时检查Authorization头,如果存在且是Bearer类型,就提取出token,并验证其有效性。
小明:那登录接口该怎么写呢?
老王:创建一个AuthController,处理登录请求,验证用户名和密码,如果正确则生成token并返回。
小明:好的,我来写这个控制器:
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private UserService userService;
@PostMapping("/login")
public ResponseEntity> login(@RequestBody LoginRequest request) {
User user = userService.findByUsername(request.getUsername());
if (user == null || !passwordEncoder.matches(request.getPassword(), user.getPassword())) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("用户名或密码错误");
}
String token = jwtUtil.generateToken(user.getUsername());
return ResponseEntity.ok().header("Authorization", "Bearer " + token).build();
}
}
老王:这里需要注意,用户密码是加密存储的,所以登录时需要用BCrypt来验证。
小明:对,我们在UserService中应该有一个方法,用来验证密码是否匹配。
老王:没错,可以使用Spring Security提供的BCryptPasswordEncoder。
小明:那我们还需要一个UserDetailsService来加载用户信息。
老王:对,创建一个UserDetailsServiceImpl类,实现UserDetailsService接口。
小明:好的,我来写这个类:
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
true,
true,
true,
true,
getAuthorities(user.getRole()));
}
private Collection extends GrantedAuthority> getAuthorities(String role) {
return Arrays.asList(new SimpleGrantedAuthority(role));
}
}

老王:这个类从数据库中查找用户,并返回一个UserDetails对象,供Spring Security使用。
小明:现在登录模块的基本功能已经完成了,接下来我们还要考虑一些安全性问题,比如防止暴力破解、限制登录次数等。
老王:对,可以在登录失败时记录日志,并设置一个冷却时间,防止频繁尝试。
小明:明白了,那我们还可以在配置文件中设置一些安全相关的参数,比如密码策略、登录尝试次数限制等。
老王:没错,这些都是提升系统安全性的重要措施。
小明:看来登录模块虽然看起来简单,但背后有很多细节需要注意。通过这次实践,我对Spring Security和JWT有了更深入的理解。
老王:是的,这也是一个很好的学习机会。希望你们的教材征订系统能顺利上线,为农业大学的师生提供更好的服务。
本站知识库部分内容及素材来源于互联网,如有侵权,联系必删!

