WEB应用前后端分离实践 关键词:rest架构+跨域访问+JWT校验 -CSDN论坛 - 001

           以往一直习惯将技术资料整理到有道笔记上,最近临时搭建了一个WEB应用,相关技术点在网上搜罗了大半天,很多都是支离破碎,走了不少弯路,不过最终还是成功了。遂考虑将这次实践过程分享出来,技术的根本是为了解决问题, 但技术的进步关键还是要靠分享,帖子比较长,看官自行找重点。
         话不多说,直接进入正题。最近半个月用了洪荒之力开发了一套基于前后端分离架构的企业级的商户网站。为什么要采用前后端分离技术,很简单,目的是为了让后端人员专注做服务接口设计,前端人员专注用户体验设计,不像ERP系统那样禁锢与一贯的页面风格。
项目具体采用以下技术:
服务框架: jersey+mybaties     这个框架一般用的不多,但是作为rest框架而言,与SPRING只是大同小异,但是代码的规范性更强,因为是纯粹的接口框架。
登录缓存控制: JWT           JWT曾和SESSION有过不少争议。session的原理在于由服务端保存用户的sessionid,弊端就是会占用服务器大量内存,所以在大型项目当中会借用缓存工具进行存储。JWT的方式是将用户状态分散到了各个客户端,这样减轻了服务器的压力,也可不必使用缓存来处理,因为JWT的原理是通过将用户信息进行base64编码与加密签名来识别用户身份,当然JWT当中也允许加入用户的其他身份或者角色信息。具体JWT原理各位有兴趣可以去百度。

跨域访问机制:CORS       在解决跨域问题上,遇到了很大的坑,都是浏览器的同源策略害的,也是打算这次发帖的一个重要原因。
        先来分析一下跨域请求的一些机制:目前跨域请求大概有这么几种,个人觉得利弊如下:
       1、JSONP   这个方式对于我个人而言,确实没有一点P用, 客户端都是AJAX的POST请求,然后JSONP只支持GET请求方式,所以放弃使用
        2、iframe    很久以前做前端的时候,友人就告诫我能不用iframe的时候尽量少用。毕竟商户端网站后期的改造还是比较复杂,不排除会出现什么其他的坑。
       3、proxy代理   曾考虑在跨域无法访问的情况下,做一个springMvc工程作为代理跳转,不过这样做,要同时维护两个项目,在成本上不到万不得已不提倡。
       4、cors        最终选择这种方式,是因为配置上比较简单,而网上很多帖子都是通过cors实现跨域功能,但在实际调试过程中也踩过不少坑。后面慢慢介绍。
先说坑,关键字:
**1、跨域功能在服务器上无法实现(本地正常)
2、nginx配置自定义header过滤(本地正常)
3、登录过滤器问题
4、jersey文件上传带参数获取**
下面我分三点把项目情况描述一下:
一、JWT用户校验
     机制是这样,用户第一次登陆系统,根据用户相关信息,生成一串BASE64编码作为用户token,返回给客户端,客户端每次请求将这个token添加到request 请求头当中,系统过滤器通过对token的校验来验证用户是否合法。这里面存在两个问题:1、用户注销系统如何操作   2、token在请求头当中如何传输。直接看代码:
      (一)、jar包引用:
        <dependency>
              <groupId>io.jsonwebtoken</groupId>
              <artifactId>jjwt</artifactId>
              <version>0.6.0</version>
        </dependency>
      (二)、登录方法:
    

Java code?

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

@POST

@Path`("/loginUser")`

@Produces`(MediaType.APPLICATION_JSON)`

@Consumes`(MediaType.APPLICATION_JSON)`

public Response loginUser(String resquestJsonBody,`@Context HttpServletRequest request)`

{

logger.info(String.format(`"Request IP--%s;requestJsonBody--%s"`, IpUtils.getIpAddr(request), resquestJsonBody));

ZyLoginResponseVo zyLoginResponseVo = `new ZyLoginResponseVo();`

String jsonRequestStr = RequestUtils.str2Json(resquestJsonBody);

ZyLoginRequestVo zyLoginRequestVo = JSONObject.parseObject(jsonRequestStr, ZyLoginRequestVo.`class`);

Map<String,Object> loginResult = zyLoginApiService.loginByUserName(zyLoginRequestVo);

int code = Integer.parseInt(String.valueOf(loginResult.get(`"loginCode"`)));

if (loginResult.get(`"loginCode"`).equals(Constants.AUTH_RESULT_SUCCESS))

{

//查询用户信息

ZyUserDto resultDto= zyLoginApiService.findUserByName(zyLoginRequestVo.getUser_name());

if`(`null == resultDto){

//用户不存在

zyLoginResponseVo.setCode(code);

zyLoginResponseVo.setDesc(`"User is not exists!!"`);

logger.info(`"The responseJsonObject:" + JSONObject.toJSONString(zyLoginResponseVo));`

return Response.status(`200).entity(JSONObject.toJSONString(zyLoginResponseVo)).type(new MediaType(CommonStr.APPLICATION, CommonStr.JSON, CommonStr.UTF8)).build();`

}

//组装缓存数据

String userName = String.valueOf(loginResult.get(`"userName"`));

String mobilePhone = String.valueOf(loginResult.get(`"mobilePhone"`));

Map<String,Object> claims = `new HashMap<String,Object>();`

claims.put(`"acessToken", loginResult.get("accessToken"`));

claims.put(`"companyname"`, resultDto.getCompanyname());

if`(StringUtils.isEmpty(userName)){`

claims.put(`"name"`, resultDto.getUsername());

}`else`{

claims.put(`"name"`, userName);

}

if`(StringUtils.isEmpty(mobilePhone)){`

claims.put(`"phone"`, resultDto.getPhone());

}`else`{

claims.put(`"phone"`, mobilePhone);

}

claims.put(`"status"`, resultDto.getStatus());

claims.put(`"companykey"`, resultDto.getCompanykey());

claims.put(`"companyid"`, resultDto.getCompanyid());

claims.put(`"id"`, resultDto.getId());

// 设置这个token的生命时间

Date expiry = getExpiryDate(`30 24` `* 60);//30天的有效日期`

// 使用Token工具类得到token,生成的策略是利用用户的姓名,到期时间,和私钥

// 使用Key key =MacProvider.generateKey(SignatureAlgorithm.HS512);

// HS512签名算法,必须保存生成的这个key到硬盘上,不然下次会出错,因为是hash算法,所以会变

String userToken = TokenUtil.getJWTString(zyLoginRequestVo.getUser_name(), expiry,KeyUtil.getKey(context),resultDto.getCompanyid(),claims);

logger.info(`"login Success, Token is:" + userToken+"; user is :"+userName);`

System.err.println(`"token is :"`+userToken);

zyLoginResponseVo.setCode(code);

zyLoginResponseVo.createInstanceData().setToken(userToken);

zyLoginResponseVo.setDesc(`"登录成功!"`);

}`else`{

String desc = ResultCodeDescUtil.getResultDesc(code);

zyLoginResponseVo.setCode(code);

zyLoginResponseVo.setDesc(desc);

}

logger.info(`"The responseJsonObject:" + JSONObject.toJSONString(zyLoginResponseVo));`

return Response.status(`200).entity(JSONObject.toJSONString(zyLoginResponseVo)).type(new MediaType(CommonStr.APPLICATION, CommonStr.JSON, CommonStr.UTF8)).build();`

}

      (一)、TOKEN 工具类
包含创建token,获取token附带信息。这里是把key文件保存在了项目当中,这个key文件只能生成一次,用户验证是通过这个key文件来处理,实际上是一串序列化的字符串。也可以改造成放入到数据库当中。

Java code?

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

112

113

114

115

116

117

118

119

120

package ichano.developer.util;`/**`

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.Jws;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

import java.security.Key;

import java.util.Date;

import java.util.Map;

import javax.annotation.security.PermitAll;

/** 

* @ClassName: TokenUtil 

* @Description: TODO(token管理工具类) 

* @author lizhijun

* @date 2016年8月11日 下午12:57:44 

*  

*/

@PermitAll

public class TokenUtil {

public static String getJWTString(String tel,Date expires,Key key,String commpanyId,Map<String,Object> claims){

if (tel == `null`) {

throw new NullPointerException(`"null username is illegal"`);

}

if (expires == `null`) {

throw new NullPointerException(`"null expires is illegal"`);

}

if (key == `null`) {

throw new NullPointerException(`"null key is illegal"`);

}

SignatureAlgorithm signatureAlgorithm =SignatureAlgorithm.HS256;

String jwtString = Jwts.builder()

.setIssuer(`"Jersey-Security-Basic"`)

.setSubject(tel)

.setAudience(`"user"`)

.setExpiration(expires)

.setClaims(claims)

.setIssuedAt(`new Date())`

.setId(commpanyId)

.signWith(signatureAlgorithm,key)

.compact();

return jwtString;

}

public static boolean isValid(String token, Key key) {

try {

Jwts.parser().setSigningKey(key).parseClaimsJws(token.trim());

return true`;`

`catch (Exception e) {`

return false`;`

}

}

public static String getName(String jwsToken, Key key) {

if (isValid(jwsToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(jwsToken);

String name = String.valueOf(claimsJws.getBody().get(`"name"`));

return name;

}

return null`;`

}

public static String[] getRoles(String jwsToken, Key key) {

if (isValid(jwsToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(jwsToken);

return claimsJws.getBody().getAudience().split(`","`);

}

return new String[]{};

}

public static int getVersion(String jwsToken, Key key) {

if (isValid(jwsToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(jwsToken);

return Integer.parseInt(claimsJws.getBody().getId());

}

return -`1`;

}

/** 

* @Title: getCompanyId 

* @Description: TODO(获取企业ID) 

* @param @param jwsToken

* @param @param key

* @param @return    设定文件 

* @return String    返回类型 

* @throws 

*/

public static String getCompanyId(String jwsToken, Key key) {

if (isValid(jwsToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(jwsToken);

String companyid = String.valueOf(claimsJws.getBody().get(`"id"`));

return companyid;

}

return null`;`

}

/** 

* @Title: setAcccessToken 

* @Description: TODO(存放鉴权中心token) 

* @param @param accessToken

* @param @param key

* @param @return    设定文件 

* @return String    返回类型 

* @throws 

*/

public static void setAcccessToken(String authToken, Key key,String accessToken) {

if (isValid(authToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(authToken);

claimsJws.getBody().put(`"acessToken"`, accessToken);

}

}

/** 

* @Title: getCompanyId 

* @Description: TODO(获取鉴权中心Token) 

* @param @param jwsToken

* @param @param key

* @param @return    设定文件 

* @return String    返回类型 

* @throws 

*/

public static String getAccessToken(String jwsToken, Key key) {

if (isValid(jwsToken, key)) {

Jws<Claims> claimsJws = Jwts.parser().setSigningKey(key).parseClaimsJws(jwsToken);

return claimsJws.getBody().getSubject();

}

return null`;`

}

}


Original url: Access
Created at: 2020-09-02 17:58:51
Category: default
Tags: none

请先后发表评论
  • 最新评论
  • 总共0条评论