您现在的位置是:亿华云 > 域名

如何使用Spring Boot 2.x构建Web服务

亿华云2025-10-08 23:29:04【域名】0人已围观

简介译者 | 卢鑫旺审校 | 梁策 孙淑娟架构:MVC架构基于JWT的身份认证Spring Data (JPA)应用用户密码加密数据库密码加密SQL ServerSlf4j基于Swagger的API文档库

译者 | 卢鑫旺

审校 | 梁策 孙淑娟

架构:

MVC架构基于JWT的何使身份认证Spring Data (JPA)应用用户密码加密数据库密码加密SQL ServerSlf4j基于Swagger的API文档

库:

应用源代码数据库的SQL脚本以及关键数据包含数据库配置信息的DB.txt文件用于测试Web服务的Postman JSON脚本

运行应用的步骤

安装JDK11或最新版本克隆项目库到本地Git地址:https://github.com/VishnuViswam/sample-web-service.git安装SQL server 2012创建应用数据库和用户插入数据库密钥数据将数据库密码的解码密钥添加到系统变量,它位于DB.txt文件中有时可能需要重新启动窗口以获取更新后的构建系统变量运行项目源代码导入预先提供的postman JSON脚本到postman客户端,调用Web服务

关于项目配置

Web服务声明

应用程序的何使每个Web服务都将在controller层中声明。

示例1.@RequestMapping("/api/v1/user")

2.@RestController

3.@Validated

4.public class UserController {

5.

6.    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

7.

8.    @Autowired

9.    private GeneralServices generalServices;

10.

11.    @Autowired

12.    private UserService userService;

13.

14.    /

**

15.     * Web service to create new user

16.     

*

17.     * @param httpServletRequest

18.     * @param user

19.     * @return

20.     */

21.    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE,构建 produces = MediaType.APPLICATION_JSON_VALUE)

22.    public ResponseEntity

23.                                             @Valid @RequestBody UserCreateModel user) {

24.        logger.debug("<--- Service to save new user request : received --->");

25.        ApiSuccessResponse apiResponse = userService.createUser(user, generalServices.getApiRequestedUserId(httpServletRequest));

26.        logger.debug("<--- Service to save new user response : given --->");

27.        return ResponseEntity.status(HttpStatus.CREATED).body(apiResponse);

28.

29.    }

30.

31.}@RequestMapping("/api/v1/user")注解用来声明Web服务的类别@RestController注解配置该类来接收Restful的 Web服务调用@PostMapping()注解决定了HTTP请求类型consume和consume标记来确定HTTP请求和响应的内容类型

通过controller层,API请求将被带到服务层。何使所有业务逻辑都将在这里处理,构建然后它将使用JPA与数据库通信。何使

通用错误处理

每当异常发生时,构建它将从相应的何使类抛出,并在CommonExceptionHandlingController中处理。构建我们必须分别处理每种异常类型。何使这个功能是构建在ControllerAdvice注解的帮助下执行的。

示例1.@ControllerAdvice

2.public class CommonExceptionHandlingController extends ResponseEntityExceptionHandler {

3.

4.    private static final Logger logger = 

5.          LoggerFactory.getLogger(CommonExceptionHandlingController.class);

6.

7.    @Override

8.    protected ResponseEntity

9.                                                                         HttpHeaders headers,何使 HttpStatus status, WebRequest request) {

10.        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(new ApiErrorResponse(Constants.WRONG_HTTP_METHOD,

11.                Constants.WRONG_HTTP_METHOD_ERROR_MESSAGE, Calendar.getInstance().getTimeInMillis()));

12.    }

13.

14.    @Override

15.    protected ResponseEntity

16.                                                                  HttpHeaders headers, HttpStatus status, WebRequest request) {

17.        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(new ApiErrorResponse(Constants.MANDATORY_FIELDS_ARE_NOT_PRESENT_CODE,

18.                Constants.MANDATORY_FIELDS_ARE_NOT_PRESENT_ERROR_MESSAGE, Calendar.getInstance().getTimeInMillis()));

19.    }

Spring Data(JPA)配置

应用程序与数据库的所有交互都将由JPA处理JPA将为应用程序中的所有逻辑对象提供一个Entity类和一个相应的Repository接口Entity类1.@Entity

2.@Table(name = "tbl_users")

3.public class Users implements Serializable {

4.    private static final long serialVersionUID = 1L;

5.

6.    @Id

7.    @GeneratedValue(strategy = GenerationType.IDENTITY)

8.    @Column(name = "id", columnDefinition = "bigint")

9.    private Long id;

10.

11.    @OneToOne(fetch = FetchType.EAGER)

12.    @JoinColumn(name = "user_account_id", columnDefinition = "bigint", nullable = false)

13.    private UserAccounts userAccount;Repository接口1.public interface UserRepository extends JpaRepository {

2.

3.    /

**

4.     * To find user object using username

5.     

*

6.     * @param username

7.     * @return

8.     */

9.    Users findByUserAccountUsername(String username);其他的JPA配置将会在application.properties文件中完成在application.properties中的网站模板JPA数据库配置1.spring.jpa.show-sql=false

2.spring.jpa.hibernate.dialect=org.hibernate.dialect.SQLServer2012Dialect

3.spring.jpa.hibernate.ddl-auto = update

4.

5.spring.jpa.properties.hibernate.show_sql=false

6.spring.jpa.properties.hibernate.format_sql=false

7.spring.jpa.properties.hibernate.use_sql=true

8.spring.jpa.open-in-view=false

9.spring.jpa.properties.hibernate.hbm2ddl.auto=update

10.spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

11.spring.jpa.hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider

数据库配置

数据库名称写在application.properties文件中其他信息(比如URL连接地址和账号密码)将写到另外两个不同属性文件中

application-dev.properties

此处写开发环境的配置信息

application-pro.properties

此处写生产环境的配置信息

spring.profiles.active=dev上述提到的属性配置写到application.properties文件中它将决定系统使用哪个子配置文件(开发环境还是生产环境)application.properties1.#DB config

2.spring.datasource.driverClassName=com.microsoft.sqlserver.jdbc.SQLServerDriverapplication-dev.properties1.#DB config

2.spring.datasource.url=jdbc:sqlserver://localhost:1433;databaseName=sample_webservice_db_dev

3.spring.datasource.username=dbuser

4.spring.datasource.password=ENC(tZTfehMYyz4EO0F0uY8fZItE7K35RtkA)

5.#spring.datasource.username=dbuser

6.#spring.datasource.password=dbuserpasswordapplication-pro.properties1.#DB config

2.spring.datasource.url=jdbc:sqlserver://192.168.1.119:1433;databaseName=sample_webservice_db

3.spring.datasource.username=proUser

4.spring.datasource.password=ENC(proUserPswd)

数据库密码加密

应用程序数据库密码会使用加密密钥通过__Jasypt __ 库加密。加密密钥需要添加到系统环境变量中的构建JASYPT_ENCRYPTOR_PASSWORD变量中。必须在属性文件中声明加密后的何使数据库密码。如此,系统就会了解密码需要解密,而解密则需要使用添加在系统变量中的密钥来进行。1.spring.datasource.password=ENC(tZTfehMYyz4EO0F0uY8fZItE7K35RtkA)对于__Jasypt __加密,我们在属性文件中使用默认的加密配置,如下所示:1.jasypt.encryptor.algorithm=PBEWithMD5AndDES

2.jasypt.encryptor.iv-generator-classname=org.jasypt.iv.NoIvGenerator我们也可以在应用的main方法中使用@EnableEncryptableProperties注解,规定数据库密码的加密配置SampleWebservice.java1.@SpringBootApplication

2.@EnableEncryptableProperties

3.public class SampleWebservice extends SpringBootServletInitializer {

4.--------

5.--------

JWT身份验证配置

使用Spring Security实现基于JSON Web令牌的身份验证当用户登录成功时,我们会创建两个token(accessToken 和 refreshToken)并把他们返回给客户端accessToken由私钥,过期时间(1小时),用户ID和角色名生成refreshToken由私钥,过期时间(24小时),用户ID和角色名生成登陆成功后,每个API请求都需要在请求头Header中的Authorization键中添加accessToken在accessToken开头添加"bearer"字符串即为”bearer accessToken” accessToken将会监控每一个Web服务请求如果accessToken过期,系统会以HTTP 401状态码回复请求此时客户端需要使用refreshToken重新获取accessToken然后我们会检查refreshToken的有效性,如果没有过期则会生成一个新的accessToken和refreshToken客户端会继续使用这些新令牌如果refreshToken也过期了,就需要用户使用账号密码重新登陆了

创建令牌的亿华云计算过程

UnAuthorisedAccessServiceImpl.java1.@Override

2.    public ApiSuccessResponse userLoginService(String username, String password) {

3.        Tokens tokens = null;

4.        Users user = userService.findByUsername(username);

5.        if (user != null) {

6.            if (passwordEncryptingService.matches(password,

7.                    user.getUserAccount().getPassword())) {

8.                if (user.getUserAccount().getStatus() == Constants.ACTIVE_STATUS) {

9.                    String roleName = user.getUserAccount().getUserRole().getRoleName();

10.                    // Creating new tokens

11.                    try {

12.                        tokens = createTokens(user.getUserAccount().getId().toString(), roleName);

13.                    } catch (Exception exception) {

14.                        logger.error("Token creation failed : ", exception);

15.                        throw new UnknownException();

16.                    }

17.

18.                    // Validating tokens

19.                    if (validationService.validateTokens(tokens)) {

20.                        tokens.setUserId(user.getUserAccount().getId());

21.                        return new ApiSuccessResponse(tokens);

22.

23.                    } else {

24.                        throw new UnknownException();

25.                    }

26.

27.                } else {

28.                    return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USER_ACCOUNT_IS_INACTIVE_ERROR_CODE,

29.                            Constants.USER_ACCOUNT_IS_INACTIVE_ERROR_MESSAGE));

30.                }

31.

32.            } else {

33.                return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_CODE,

34.                        Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_MESSAGE));

35.            }

36.

37.        } else {

38.            return new ApiSuccessResponse(new ApiResponseWithCode(Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_CODE,

39.                    Constants.USERNAME_OR_PASSWORD_IS_INCORRECT_ERROR_MESSAGE));

40.        }

41.    }

42.

43.    @Override

44.    public ApiSuccessResponse createNewAccessTokenUsingRefreshToken(String refreshToken) {

45.        Tokens tokens = null;

46.        UserAccounts userAccount = null;

47.        AppConfigSettings configSettings = appConfigSettingsService.findByConfigKeyAndStatus(Constants.JWT_SECRET_KEY,

48.                Constants.ACTIVE_STATUS);

49.        // Validate Refresh token

50.        userAccount = jwtTokenHandler.validate(configSettings.getConfigValue(), refreshToken);

51.        if (userAccount != null) {

52.            // Creating new tokens if provided refresh token is valid

53.            try {

54.                tokens = createTokens(userAccount.getId().toString(), userAccount.getRole());

55.            } catch (Exception exception) {

56.                logger.error("Token creation failed : ", exception);

57.                throw new UnknownException();

58.            }

59.            if (validationService.validateTokens(tokens)) {

60.                tokens.setUserId(userAccount.getId());

61.                return new ApiSuccessResponse(tokens);

62.

63.            } else {

64.                throw new UnknownException();

65.            }

66.        } else {

67.            return new ApiSuccessResponse(new ApiResponseWithCode(Constants.REFRESH_TOKEN_EXPIRED_ERROR_CODE,

68.                    Constants.REFRESH_TOKEN_EXPIRED_ERROR_MESSAGE));

69.        }

70.    }上述代码中的userLoginService方法会检查用户凭据,如果有效则颁发令牌。CreateNewAccessTokenUsingRefreshToken方法会在refreshToken验证成功后,生成新的accessToken和refreshToken。过滤和验证令牌的过程WebConfig.java1.@Configuration

2.@EnableWebSecurity

3.@EnableGlobalMethodSecurity(prePostEnabled = true)

4.public class WebConfig extends WebSecurityConfigurerAdapter {

5.

6.    @Autowired

7.    private JwtAuthenticationProvider authenticationProvider;

8.

9.    @Autowired

10.    private JwtAuthenticationEntryPoint entryPoint;

11.

12.    @Bean

13.    public AuthenticationManager authenticationManager() {

14.        return new ProviderManager(Collections.singletonList(authenticationProvider));

15.    }

16.

17.    @Bean

18.    public JwtAuthenticationTokenFilter authenticationTokenFilter() {

19.        JwtAuthenticationTokenFilter filter = new JwtAuthenticationTokenFilter();

20.        filter.setAuthenticationManager(authenticationManager());

21.        filter.setAuthenticationSuccessHandler(new JwtSuccessHandler());

22.        return filter;

23.    }

24.

25.    @Override

26.    protected void configure(HttpSecurity http) throws Exception {

27.        http.csrf().disable()

28.                .exceptionHandling().authenticationEntryPoint(entryPoint).and().sessionManagement()

29.                .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

30.                .addFilterBefore(new WebSecurityCorsFilter(), ChannelProcessingFilter.class)

31.                .addFilterBefore(authenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)

32.                .headers().cacheControl();

33.    }

34.}这个配置将使用@EnableWebSecurity和@EnableGlobalMethodSecurity(prePostEnabled = true)两个注解来启用spring security模块这里,我们将把JWT过滤器注入到系统的HTTP请求中JwtAuthenticationTokenFilter.java1.public class JwtAuthenticationTokenFilter extends AbstractAuthenticationProcessingFilter {

2.

3.    private final Logger logger = LoggerFactory.getLogger(this.getClass());

4.

5.    @Autowired

6.    private GeneralServices generalServices;

7.

8.    public JwtAuthenticationTokenFilter() {

9.        super("/api/**");

10.    }

11.

12.    @Override

13.    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest,

14.                                                HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {

15.                                                  -------

16.                                                  --------

17.    }在如上的类中,JwtAuthenticationTokenFilter()的方法将过滤所有URL中含有“api”命名关键字的Web服务请求所有经过过滤的Web服务请求将到达attemptAuthentication方法我们可以在这个方法里处理所有的业务逻辑

应用用户密码加密

该应用中所有的用户密码都将会使用BCrypt加密PasswordEncryptingService.java1.public class PasswordEncryptingService {

2.

3.    public String encode(CharSequence rawPassword) {

4.        return BCrypt.hashpw(rawPassword.toString(), BCrypt.gensalt(6));

5.    }

6.

7.    public boolean matches(CharSequence rawPassword, String encodedPassword) {

8.        return BCrypt.checkpw(rawPassword.toString(), encodedPassword);

9.    }这里,encode方法用来加密密码matches方法用来交叉检查提供的密码和用户的实际密码

使用Slf4j配置日志

在一个叫logback-spring.xml的文件中配置日志为了记录每个类的日志,我们需要在相应的类中注入Slf4j示例UserServiceImpl.java1.@Service("UserService")

2.@Scope("prototype")

3.public class UserServiceImpl implements UserService {

4.    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);上面的代码片段显示了我们如何将类注入到logger以下是记录日志的一些基本方法

logger.error("Error");

logger.info("Info");

logger.warn("Warn");

基于Swagger的API文档

API文档在Web服务应用程序中扮演着重要的角色之前,我们使用静态Excel文档创建API文档这个库将帮助我们在应用程序中使用注释创建API文档Pom.xml1.  

2.            io.springfox

3.            springfox-boot-starter

4.            ${ springfox.swagger.version}

5.        

6.

7.        

8.            io.springfox

9.            springfox-swagger-ui

10.            ${ springfox.swagger.version}

11.        为了集成Swagger,上述这些是我们要在pom文件中添加的库我们需要在应用程序中做一些配置来启用API文档SwaggerAPIDocConfig.java1.@Configuration

2.@EnableSwagger2

3.public class SwaggerAPIDocConfig {

4.

5.    public static final Contact DEFAULT_CONTACT = new Contact("Demo", "http://www.demo.ae/",

6.            "info@demo.ae");

7.

8.    public static final ApiInfo DEFAUL_API_INFO = new ApiInfo("Sample Application",

9.            "Sample Application description.",

10.            "1.0.0",

11.            "http://www.sampleapplication.ae/",

12.            DEFAULT_CONTACT, "Open licence",

13.            "http://www.sampleapplication.ae/#license",

14.            new ArrayList());

15.

16.    private static final Set DEFAULT_PRODICERS_AND_CONSUMERS =

17.            new HashSet<>(Arrays.asList("application/json", "application/xml"));

18.

19.    @Bean

20.    public Docket api() {

21.        return new Docket(DocumentationType.SWAGGER_2)

22.                .apiInfo(DEFAUL_API_INFO)

23.                .produces(DEFAULT_PRODICERS_AND_CONSUMERS)

24.                .consumes(DEFAULT_PRODICERS_AND_CONSUMERS)

25.                .select()

26.                .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))

27.                .paths(PathSelectors.any())

28.                .build();

29.    }

30.}正如在上边的b2b供应网类中看到的,我们需要添加关于项目的基本信息我们需要告诉Swagger从哪个类创建API文档,这是在.apis(RequestHandlerSelectors.withClassAnnotation,(RestController.class))命名行下配置的我们可以通过http://localhost:8080/sampleWebService/apidoc来访问Swagger的API文档

Postman脚本

我们可以在代码库中找到2个Postman JSON脚本,然后将它们导入到Postman客户端首先执行登陆的Web服务请求,然后执行其余web服务请求

原文链接:​​How To Build Web Service Using Spring Boot 2.x​​,作者:Vishnu Viswambharan

很赞哦!(317)