利用Spring Boot如何开发REST服务详解

2025-05-29 0 33

REST服务介绍

RESTful service是一种架构模式,近几年比较流行了,它的轻量级web服务,发挥HTTP协议的原生的GET,PUT,POST,DELETE。 REST模式的Web服务与复杂的SOAP和XML-RPC对比来讲明显的更加简洁,越来越多的web服务开始采用REST风格设计和实现。例如,Amazon.com提供接近REST风格的Web服务进行图书查找;雅虎提供的Web服务也是REST风格的。REST 并非始终是正确的选择。 它作为一种设计 Web 服务的方法而变得流行,这种方法对专有中间件(例如某个应用程序服务器)的依赖比基于 SOAP 和 WSDL 的方法更少。 在某种意义上,通过强调URI和HTTP等早期 Internet 标准,REST 是对大型应用程序服务器时代之前的 Web 方式的回归。

如下图示例:

利用Spring Boot如何开发REST服务详解

使用REST的关键是如何抽象资源,抽象得越精确,对REST的应用就越好。

REST服务关键原则:

1. 给一切物体一个ID

2.连接物体在一起

3.使用标准方法

4.资源多重表述

5.无状态通信

本文介绍如何基于Spring Boot搭建一个简易的REST服务框架,以及如何通过自定义注解实现Rest服务鉴权

搭建框架

pom.xml

首先,引入相关依赖,数据库使用mongodb,同时使用redis做缓存

注意:这里没有使用tomcat,而是使用undertow

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26
<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter</artifactId>

</dependency>

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-test</artifactId>

<scope>test</scope>

</dependency>

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

<exclusions>

<exclusion> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-tomcat</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-undertow</artifactId>

</dependency>

<!--redis支持-->

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

<!--mongodb支持-->

<dependency> <groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-mongodb</artifactId>

</dependency>

引入spring-boot-starter-web支持web服务

引入spring-boot-starter-data-redis 和spring-boot-starter-data-mongodb就可以方便的使用mongodb和redis了

配置文件

profiles功能

为了方便 区分开发环境和线上环境,可以使用profiles功能,在application.properties里增加
spring.profiles.active=dev

然后增加application-dev.properties作为dev配置文件。

mondb配置

配置数据库地址即可

?

1
spring.data.mongodb.uri=mongodb://ip:port/database?readPreference=primaryPreferred

redis配置

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
spring.redis.database=0

# Redis服务器地址

spring.redis.host=ip

# Redis服务器连接端口

spring.redis.port=6379

# Redis服务器连接密码(默认为空)

spring.redis.password=

# 连接池最大连接数(使用负值表示没有限制)

spring.redis.pool.max-active=8

# 连接池最大阻塞等待时间(使用负值表示没有限制)

spring.redis.pool.max-wait=-1

# 连接池中的最大空闲连接

spring.redis.pool.max-idle=8

# 连接池中的最小空闲连接

spring.redis.pool.min-idle=0

# 连接超时时间(毫秒)

spring.redis.timeout=0

数据访问

mongdb

mongdb访问很简单,直接定义接口extends MongoRepository即可,另外可以支持JPA语法,例如:

?

1

2

3

4
@Component

public interface UserRepository extends MongoRepository<User, Integer> {

public User findByUserName(String userName);

}

使用时,加上@Autowired注解即可。

?

1

2

3

4

5
@Component

public class AuthService extends BaseService {

@Autowired

UserRepository userRepository;

}

Redis访问

使用StringRedisTemplate即可直接访问Redis

?

1

2

3

4

5

6

7

8
@Component

public class BaseService {

@Autowired

protected MongoTemplate mongoTemplate;

@Autowired

protected StringRedisTemplate stringRedisTemplate;

}

储存数据:

?

1
.stringRedisTemplate.opsForValue().set(token_key, user.getId()+"",token_max_age, TimeUnit.SECONDS);

删除数据:

?

1
stringRedisTemplate.delete(getFormatToken(accessToken,platform));

Web服务

定义一个Controller类,加上RestController即可,使用RequestMapping用来设置url route

?

1

2

3

4

5

6

7

8
@RestController

public class AuthController extends BaseController {

@RequestMapping(value = {"/"}, produces = "application/json;charset=utf-8", method = {RequestMethod.GET, RequestMethod.POST})

@ResponseBody

public String main() {

return "hello world!";

}

}

现在启动,应该就能看到hello world!了

服务鉴权

简易accessToken机制

提供登录接口,认证成功后,生成一个accessToken,以后访问接口时,带上accessToken,服务端通过accessToken来判断是否是合法用户。

为了方便,可以将accessToken存入redis,设定有效期。

?

1

2

3
String token = EncryptionUtils.sha256Hex(String.format("%s%s", user.getUserName(), System.currentTimeMillis()));

String token_key = getFormatToken(token, platform);

this.stringRedisTemplate.opsForValue().set(token_key, user.getId()+"",token_max_age, TimeUnit.SECONDS);

拦截器身份认证

为了方便做统一的身份认证,可以基于Spring的拦截器机制,创建一个拦截器来做统一认证。

?

1

2
public class AuthCheckInterceptor implements HandlerInterceptor {

}

要使拦截器生效,还需要一步,增加配置:

?

1

2

3

4

5

6

7

8

9

10

11
@Configuration

public class SessionConfiguration extends WebMvcConfigurerAdapter {

@Autowired

AuthCheckInterceptor authCheckInterceptor;

@Override

public void addInterceptors(InterceptorRegistry registry) {

super.addInterceptors(registry);

// 添加拦截器

registry.addInterceptor(authCheckInterceptor).addPathPatterns("/**");

}

}

自定义认证注解

为了精细化权限认证,比如有的接口只能具有特定权限的人才能访问,可以通过自定义注解轻松解决。在自定义的注解里,加上roles即可。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14
/**

* 权限检验注解

*/

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface AuthCheck {

/**

* 角色列表

* @return

*/

String[] roles() default {};

}

检验逻辑:

只要接口加上了AuthCheck注解,就必须是登陆用户

如果指定了roles,则除了登录外,用户还应该具备相应的角色。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56
String[] ignoreUrls = new String[]{

"/user/.*",

"/cat/.*",

"/app/.*",

"/error"

};

public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception {

// 0 检验公共参数

if(!checkParams("platform",httpServletRequest,httpServletResponse)){

return false;

}

// 1、忽略验证的URL

String url = httpServletRequest.getRequestURI().toString();

for(String ignoreUrl :ignoreUrls){

if(url.matches(ignoreUrl)){

return true;

}

}

// 2、查询验证注解

HandlerMethod handlerMethod = (HandlerMethod) handler;

Method method = handlerMethod.getMethod();

// 查询注解

AuthCheck authCheck = method.getAnnotation(AuthCheck.class);

if (authCheck == null) {

// 无注解,不需要

return true;

}

// 3、有注解,先检查accessToken

if(!checkParams("accessToken",httpServletRequest,httpServletResponse)){

return false;

}

// 检验token是否过期

Integer userId = authService.getUserIdFromToken(httpServletRequest.getParameter("accessToken"),

httpServletRequest.getParameter("platform"));

if(userId==null){

logger.debug("accessToken timeout");

output(ResponseResult.Builder.error("accessToken已过期").build(),httpServletResponse);

return false;

}

// 4、再检验是否包含必要的角色

if(authCheck.roles()!=null&&authCheck.roles().length>0){

User user = authService.getUser(userId);

boolean isMatch = false;

for(String role : authCheck.roles()){

if(user.getRole().getName().equals(role)){

isMatch = true;

break;

}

}

// 角色未匹配,验证失败

if(!isMatch){

return false;

}

}

return true;

}

服务响应结果封装

增加一个Builder,方便生成最终结果

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111
public class ResponseResult {

public static class Builder{

ResponseResult responseResult;

Map<String,Object> dataMap = Maps.newHashMap();

public Builder(){

this.responseResult = new ResponseResult();

}

public Builder(String state){

this.responseResult = new ResponseResult(state);

}

public static Builder newBuilder(){

return new Builder();

}

public static Builder success(){

return new Builder("success");

}

public static Builder error(String message){

Builder builder = new Builder("error");

builder.responseResult.setError(message);

return builder;

}

public Builder append(String key,Object data){

this.dataMap.put(key,data);

return this;

}

/**

* 设置列表数据

* @param datas 数据

* @return

*/

public Builder setListData(List<?> datas){

this.dataMap.put("result",datas);

this.dataMap.put("total",datas.size());

return this;

}

public Builder setData(Object data){

this.dataMap.clear();

this.responseResult.setData(data);

return this;

}

boolean wrapData = false;

/**

* 将数据包裹在data中

* @param wrapData

* @return

*/

public Builder wrap(boolean wrapData){

this.wrapData = wrapData;

return this;

}

public String build(){

JSONObject jsonObject = new JSONObject();

jsonObject.put("state",this.responseResult.getState());

if(this.responseResult.getState().equals("error")){

jsonObject.put("error",this.responseResult.getError());

}

if(this.responseResult.getData()!=null){

jsonObject.put("data", JSON.toJSON(this.responseResult.getData()));

}else if(dataMap.size()>0){

if(wrapData) {

JSONObject data = new JSONObject();

dataMap.forEach((key, value) -> {

data.put(key, value);

});

jsonObject.put("data", data);

}else{

dataMap.forEach((key, value) -> {

jsonObject.put(key, value);

});

}

}

return jsonObject.toJSONString();

}

}

private String state;

private Object data;

private String error;

public String getError() {

return error;

}

public void setError(String error) {

this.error = error;

}

public ResponseResult(){}

public ResponseResult(String rc){

this.state = rc;

}

/**

* 成功时返回

* @param rc

* @param result

*/

public ResponseResult(String rc, Object result){

this.state = rc;

this.data = result;

}

public String getState() {

return state;

}

public void setState(String state) {

this.state = state;

}

public Object getData() {

return data;

}

public void setData(Object data) {

this.data = data;

}

}

调用时可以优雅一点

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30
@RequestMapping(value = {"/user/login","/pc/user/login"}, produces = "application/json;charset=utf-8", method = {RequestMethod.GET, RequestMethod.POST})

@ResponseBody

public String login(String userName,String password,Integer platform) {

User user = this.authService.login(userName,password);

if(user!=null){

// 登陆

String token = authService.updateToken(user,platform);

return ResponseResult.Builder

.success()

.append("accessToken",token)

.append("userId",user.getId())

.build();

}

return ResponseResult.Builder.error("用户不存在或密码错误").build();

}

protected String error(String message){

return ResponseResult.Builder.error(message).build();

}

protected String success(){

return ResponseResult.Builder

.success()

.build();

}

protected String successDataList(List<?> data){

return ResponseResult.Builder

.success()

.wrap(true) // data包裹

.setListData(data)

.build();

}

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对快网idc的支持。

原文链接:http://www.cnblogs.com/xiaoqi/p/SpringBootRest.html

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

快网idc优惠网 建站教程 利用Spring Boot如何开发REST服务详解 https://www.kuaiidc.com/113942.html

相关文章

发表评论
暂无评论