科研管理系统
张伟(以下简称张):李娜,最近我们在学校的一个科研项目中要搭建一个科研管理系统,你对后端开发这块有经验,能给我讲讲怎么开始吗?
李娜(以下简称李):当然可以。首先,我们需要明确这个科研系统的功能需求。比如,用户管理、课题申报、成果录入、数据统计等模块。
张:明白了。那你觉得用什么技术栈比较合适呢?我们学校的技术团队主要是用Java的,所以可能优先考虑Java相关的框架。
李:没错,Java生态非常成熟,适合企业级应用开发。推荐使用Spring Boot框架,它简化了配置,提高了开发效率,而且和数据库、安全控制等集成起来也比较方便。
张:听起来不错。那我们可以先搭建一个基本的后端结构。你有没有具体的代码示例可以参考?
李:当然有。我可以给你写一个简单的Spring Boot项目结构,包括依赖、启动类和一个基本的REST接口。
张:太好了,我来记一下。
李:好的,下面是一个Spring Boot项目的pom.xml文件内容,包含必要的依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>research-system</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ResearchSystem</name>
<description>科研系统后端服务</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

张:这个pom文件看起来很标准。接下来是不是需要创建一个启动类?
李:是的,Spring Boot项目通常有一个主类,用来启动应用。下面是示例代码:
package com.example.researchsystem;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ResearchSystemApplication {
public static void main(String[] args) {
SpringApplication.run(ResearchSystemApplication.class, args);
}
}
张:明白了。那如何创建一个简单的REST API呢?比如获取用户信息?
李:我们可以创建一个Controller类。例如,一个UserController,提供一个GET接口来获取用户信息。
张:好的,那具体代码是怎样的?
李:以下是UserController的示例代码:
package com.example.researchsystem.controller;
import com.example.researchsystem.model.User;
import com.example.researchsystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@GetMapping
public List getAllUsers() {
return userService.getAllUsers();
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PostMapping
public User createUser(@RequestBody User user) {
return userService.createUser(user);
}
@PutMapping("/{id}")
public User updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
张:这看起来很完整。那UserService和User实体类又是怎么写的?
李:我们先来看User实体类,它通常对应数据库表结构。以下是示例代码:
package com.example.researchsystem.model;
import jakarta.persistence.*;
import lombok.Data;
@Entity
@Data
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String role;
}
张:用了Lombok,确实方便。那UserService是怎么实现的?
李:UserService是一个Service层,负责业务逻辑。这里我们使用Spring Data JPA来操作数据库。以下是示例代码:
package com.example.researchsystem.service;
import com.example.researchsystem.model.User;
import com.example.researchsystem.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
public List getAllUsers() {
return userRepository.findAll();
}
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
public User createUser(User user) {
return userRepository.save(user);
}
public User updateUser(Long id, User user) {
if (userRepository.existsById(id)) {
user.setId(id);
return userRepository.save(user);
}
return null;
}
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
张:明白了。那UserRepository又是什么?
李:UserRepository是一个接口,继承自JpaRepository,用于定义数据库操作方法。以下是代码示例:
package com.example.researchsystem.repository; import com.example.researchsystem.model.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository{ }
张:这样整个后端结构就初步完成了。那接下来要考虑的是安全性问题,比如登录认证。
李:是的,安全是科研系统中非常重要的一环。我们可以使用Spring Security来实现基于JWT的认证机制。
张:那能不能也给个代码示例?
李:当然可以。下面是一个简单的JWT认证流程的代码片段,包括生成Token、验证Token以及拦截器的配置。
张:好的,我先看看这部分代码。
李:首先是生成JWT Token的工具类:
package com.example.researchsystem.security;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
@Component
public class JwtUtil {
private final String SECRET_KEY = "your-secret-key-here";
private Key getSignKey() {
byte[] keyBytes = Decoders.BASE64.decode(SECRET_KEY);
return Keys.hmacShaKeyFor(keyBytes);
}
private String generateToken(Map claims, String subject) {
return Jwts.builder()
.setClaims(claims)
.setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
.signWith(getSignKey(), SignatureAlgorithm.HS256)
.compact();
}
public String generateToken(String username) {
return generateToken(new HashMap<>(), username);
}
public Boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
public T extractClaim(String token, Function resolver) {
final Claims claims = extractAllClaims(token);
return resolver.apply(claims);
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder().setSigningKey(getSignKey()).build().parseClaimsJws(token).getBody();
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
张:这段代码看起来挺完整的。那如何在Controller中使用它呢?
李:我们可以添加一个LoginController,处理用户登录请求,并返回JWT Token。
张:好,那代码是怎样的?
李:以下是LoginController的示例代码:
package com.example.researchsystem.controller;
import com.example.researchsystem.security.JwtUtil;
import com.example.researchsystem.model.User;
import com.example.researchsystem.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private UserService userService;
@Autowired
private JwtUtil jwtUtil;
@PostMapping("/login")
public String login(@RequestBody User user) {
User dbUser = userService.getUserByEmail(user.getEmail());
if (dbUser != null && dbUser.getPassword().equals(user.getPassword())) {
return jwtUtil.generateToken(dbUser.getUsername());
} else {
return "Invalid credentials";
}
}
}
张:明白了。那如何在其他接口中校验Token呢?
李:我们可以使用Spring Security的过滤器链,在请求到达Controller之前进行Token校验。
张:那这部分代码应该怎么写?
李:我们可以编写一个JWTFilter类,继承OncePerRequestFilter,重写doFilterInternal方法。
张:好的,那代码是怎样的?
李:以下是JWTFilter的示例代码:
package com.example.researchsystem.security;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private JwtUtil jwtUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
String token = null;
String username = null;
if (authHeader != null && authHeader.startsWith("Bearer ")) {
token = authHeader.substring(7);
username = jwtUtil.extractUsername(token);
}
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
if (jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
张:这代码应该就能实现Token校验了。那如何将它加入到Spring Security的过滤链中?
李:我们可以创建一个SecurityConfig类,继承WebSecurityConfigurerAdapter,配置相关权限。
张:好的,那代码是怎样的?
李:以下是SecurityConfig的示例代码:
package com.example.researchsystem.config;
import com.example.researchsystem.security.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFiltersOrder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private JwtFilter jwtFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated();
return http.build();
}
}
张:这样整个后端系统就具备了基础的功能和安全性。
李:是的,这只是基础部分。后续还可以扩展更多功能,比如权限管理、日志记录、API文档生成等。
张:感谢你的讲解,我现在对科研系统的后端开发有了更清晰的认识。
李:不客气,希望这些内容对你有帮助。如果有更多问题,随时问我。