Spring-boot结合Shrio实现JWT的方法

2025-05-29 0 38

本文介绍了spring-boot结合shrio实现jwt的方法,分享给大家,具体如下:

关于验证大致分为两个方面:

  1. 用户登录时的验证;
  2. 用户登录后每次访问时的权限认证

主要解决方法:使用自定义的shiro filter

项目搭建:

这是一个spring-boot 的web项目,不了解spring-boot的项目搭建,请google。

pom.mx引入相关jar包

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17
<!-- shiro 权限管理 -->

<dependency>

<groupid>org.apache.shiro</groupid>

<artifactid>shiro-spring</artifactid>

<version>${shiro.version}</version>

</dependency>

<dependency>

<groupid>org.apache.shiro</groupid>

<artifactid>shiro-core</artifactid>

<version>${shiro.version}</version>

</dependency>

<!-- jwt -->

<dependency>

<groupid>io.jsonwebtoken</groupid>

<artifactid>jjwt</artifactid>

<version>0.9.0</version>

</dependency>

shrio 的相关配置

划重点!!自定义了一个filter

?

1
filtermap.put("jwtfilter", new jwtfilter());

?

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
@configuration

public class shiroconfig {

@bean

public shirofilterfactorybean getshirofilterfactorybean(securitymanager securitymanager) {

shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean();

shirofilterfactorybean.setsecuritymanager(securitymanager);

// 添加自己的过滤器并且取名为jwtfilter

map<string, filter> filtermap = new hashmap<>();

filtermap.put("jwtfilter", new jwtfilter());

shirofilterfactorybean.setfilters(filtermap);

/*

* 自定义url规则

* http://shiro.apache.org/web.html#urls-

*/

map<string, string> filterchaindefinitionmap = shirofilterfactorybean.getfilterchaindefinitionmap();

filterchaindefinitionmap.put("/**", "jwtfilter");

shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap);

return shirofilterfactorybean;

}

/**

* securitymanager 不用直接注入shirodbrealm,可能会导致事务失效

* 解决方法见 handlecontextrefresh

* http://www.debugrun.com/a/nks9ejq.html

*/

@bean("securitymanager")

public defaultwebsecuritymanager securitymanager(tokenrealm tokenrealm) {

defaultwebsecuritymanager manager = new defaultwebsecuritymanager();

manager.setrealm(tokenrealm);

/*

* 关闭shiro自带的session,详情见文档

* http://shiro.apache.org/session-management.html#sessionmanagement-statelessapplications%28sessionless%29

*/

defaultsubjectdao subjectdao = new defaultsubjectdao();

defaultsessionstorageevaluator defaultsessionstorageevaluator = new defaultsessionstorageevaluator();

defaultsessionstorageevaluator.setsessionstorageenabled(false);

subjectdao.setsessionstorageevaluator(defaultsessionstorageevaluator);

manager.setsubjectdao(subjectdao);

return manager;

}

@bean

public lifecyclebeanpostprocessor lifecyclebeanpostprocessor() {

return new lifecyclebeanpostprocessor();

}

@bean(name = "tokenrealm")

@dependson("lifecyclebeanpostprocessor")

public tokenrealm tokenrealm() {

return new tokenrealm();

}

@bean

@dependson("lifecyclebeanpostprocessor")

public defaultadvisorautoproxycreator defaultadvisorautoproxycreator() {

defaultadvisorautoproxycreator defaultadvisorautoproxycreator = new defaultadvisorautoproxycreator();

// 强制使用cglib,防止重复代理和可能引起代理出错的问题

// https://zhuanlan.zhihu.com/p/29161098

defaultadvisorautoproxycreator.setproxytargetclass(true);

return defaultadvisorautoproxycreator;

}

@bean

public authorizationattributesourceadvisor getauthorizationattributesourceadvisor(securitymanager securitymanager) {

authorizationattributesourceadvisor authorizationattributesourceadvisor = new authorizationattributesourceadvisor();

authorizationattributesourceadvisor.setsecuritymanager(securitymanager);

return new authorizationattributesourceadvisor();

}

}

自定义shrio filter

执行顺序:prehandle -> dofilterinternal -> executelogin -> onloginsuccess

主要判断是不是登录请求的是 dofilterinternal

?

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
public class jwtfilter extends basichttpauthenticationfilter {

/**

* 自定义执行登录的方法

*/

@override

protected boolean executelogin(servletrequest request, servletresponse response) throws ioexception {

httpservletrequest httpservletrequest = (httpservletrequest) request;

usernamepasswordtoken usernamepasswordtoken = json.parseobject(httpservletrequest.getinputstream(), usernamepasswordtoken.class);

// 提交给realm进行登入,如果错误他会抛出异常并被捕获

subject subject = this.getsubject(request, response);

subject.login(usernamepasswordtoken);

return this.onloginsuccess(usernamepasswordtoken, subject, request, response);

//错误抛出异常

}

/**

* 最先执行的方法

*/

@override

protected boolean prehandle(servletrequest request, servletresponse response) throws exception {

return super.prehandle(request, response);

}

/**

* 登录成功后登录的操作

* 加上jwt 的header

*/

@override

protected boolean onloginsuccess(authenticationtoken token, subject subject, servletrequest request, servletresponse response) {

httpservletresponse httpservletresponse = (httpservletresponse) response;

string jwttoken = jwts.builder()

.setid(token.getprincipal().tostring())

.setexpiration(datetime.now().plusminutes(30).todate())

.signwith(signaturealgorithm.hs256, jwtcost.signaturekey)

.compact();

httpservletresponse.addheader(authorization_header, jwttoken);

return true;

}

/**

* 登录以及校验的主要流程

* 判断是否是登录,或者是登陆后普通的一次请求

*/

@override

public void dofilterinternal(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception {

httpservletrequest httpservletrequest = (httpservletrequest) servletrequest;

httpservletresponse httpservletresponse = (httpservletresponse) servletresponse;

string servletpath = httpservletrequest.getservletpath();

if (stringutils.equals(servletpath, "/login")) {

//执行登录

this.executelogin(servletrequest, servletresponse);

} else {

string authenticationheader = httpservletrequest.getheader(authorization_header);

if (stringutils.isnotempty(authenticationheader)) {

claims body = jwts.parser()

.setsigningkey(jwtcost.signaturekey)

.parseclaimsjws(authenticationheader)

.getbody();

if (body != null) {

//更新token

body.setexpiration(datetime.now().plusminutes(30).todate());

string updatetoken = jwts.builder().setclaims(body).compact();

httpservletresponse.addheader(authorization_header, updatetoken);

//添加用户凭证

principalcollection principals = new simpleprincipalcollection(body.getid(), jwtcost.usernamepasswordrealm);//拼装shiro用户信息

websubject.builder builder = new websubject.builder(servletrequest, servletresponse);

builder.principals(principals);

builder.authenticated(true);

builder.sessioncreationenabled(false);

websubject subject = builder.buildwebsubject();

//塞入容器,统一调用

threadcontext.bind(subject);

filterchain.dofilter(httpservletrequest, httpservletresponse);

}

} else {

httpservletresponse.setstatus(httpstatus.forbidden.value());

}

}

}

}

登录失败处理

处理shrio异常

?

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
@restcontrolleradvice

public class globalcontrollerexceptionhandler {

@exceptionhandler(value = exception.class)

public object allexceptionhandler(httpservletrequest request, httpservletresponse response, exception exception) {

string message = exception.getcause().getmessage();

logutil.error(message);

return new resultinfo(exception.getclass().getname(), message);

}

/*=========== shiro 异常拦截==============*/

@exceptionhandler(value = incorrectcredentialsexception.class)

public string incorrectcredentialsexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "incorrectcredentialsexception";

}

@exceptionhandler(value = unknownaccountexception.class)

public string unknownaccountexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "unknownaccountexception";

}

@exceptionhandler(value = lockedaccountexception.class)

public string lockedaccountexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "lockedaccountexception";

}

@exceptionhandler(value = excessiveattemptsexception.class)

public string excessiveattemptsexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "excessiveattemptsexception";

}

@exceptionhandler(value = authenticationexception.class)

public string authenticationexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "authenticationexception";

}

@exceptionhandler(value = unauthorizedexception.class)

public string unauthorizedexception(httpservletrequest request, httpservletresponse response, exception exception) {

response.setstatus(httpstatus.forbidden.value());

return "unauthorizedexception";

}

}

处理jwt异常

这是个坑,因为是在filter内发生的异常,@exceptionhandler是截获不到的。

?

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
/**

* 截获spring boot error页面

*/

@restcontroller

public class globalexceptionhandler implements errorcontroller {

@override

public string geterrorpath() {

return "/error";

}

@requestmapping(value = "/error")

public object error(httpservletrequest request, httpservletresponse response) throws exception {

// 错误处理逻辑

exception exception = (exception) request.getattribute("javax.servlet.error.exception");

throwable cause = exception.getcause();

if (cause instanceof expiredjwtexception) {

response.setstatus(httpstatus.gateway_timeout.value());

return new resultinfo("expiredjwtexception", cause.getmessage());

}

if (cause instanceof malformedjwtexception) {

response.setstatus(httpstatus.forbidden.value());

return new resultinfo("malformedjwtexception", cause.getmessage());

}

return new resultinfo(cause.getcause().getmessage(), cause.getmessage());

}

}

关于权限等授权信息,可以直接放到redis中实现缓存。我认为也是不错的。

源码奉上:githup-shiro分支:温馨提示:平时测试代码可能比较乱。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持快网idc。

原文链接:https://segmentfault.com/a/1190000014750168

收藏 (0) 打赏

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

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

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

快网idc优惠网 建站教程 Spring-boot结合Shrio实现JWT的方法 https://www.kuaiidc.com/111658.html

相关文章

发表评论
暂无评论