好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Spring Boot Security 结合 JWT 实现无状态的分布式API接口

简介

json web token(缩写 jwt)是目前最流行的跨域认证解决方案。 json web token 入门教程 这篇文章可以帮你了解jwt的概念。本文重点讲解spring boot 结合 jwt ,来实现前后端分离中,接口的安全调用。

spring security,这是一种基于 spring aop 和 servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 web 请求级和方法调用级处理身份确认和授权。

快速上手

之前的文章已经对 spring security 进行了讲解,这一节对涉及到 spring security 的配置不详细讲解。若不了解 spring security 先移步到 spring boot security 详解 。

建表

?

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

drop table if exists `user`;

drop table if exists `role`;

drop table if exists `user_role`;

drop table if exists `role_permission`;

drop table if exists `permission`;

 

create table `user` (

`id` bigint( 11 ) not null auto_increment,

`username` varchar( 255 ) not null ,

`password` varchar( 255 ) not null ,

primary key (`id`)

);

create table `role` (

`id` bigint( 11 ) not null auto_increment,

`name` varchar( 255 ) not null ,

primary key (`id`)

);

create table `user_role` (

`user_id` bigint( 11 ) not null ,

`role_id` bigint( 11 ) not null

);

create table `role_permission` (

`role_id` bigint( 11 ) not null ,

`permission_id` bigint( 11 ) not null

);

create table `permission` (

`id` bigint( 11 ) not null auto_increment,

`url` varchar( 255 ) not null ,

`name` varchar( 255 ) not null ,

`description` varchar( 255 ) null ,

`pid` bigint( 11 ) not null ,

primary key (`id`)

);

 

insert into user (id, username, password) values ( 1 , 'user' , 'e10adc3949ba59abbe56e057f20f883e' );

insert into user (id, username , password) values ( 2 , 'admin' , 'e10adc3949ba59abbe56e057f20f883e' );

insert into role (id, name) values ( 1 , 'user' );

insert into role (id, name) values ( 2 , 'admin' );

insert into permission (id, url, name, pid) values ( 1 , '/user/hi' , '' , 0 );

insert into permission (id, url, name, pid) values ( 2 , '/admin/hi' , '' , 0 );

insert into user_role (user_id, role_id) values ( 1 , 1 );

insert into user_role (user_id, role_id) values ( 2 , 1 );

insert into user_role (user_id, role_id) values ( 2 , 2 );

insert into role_permission (role_id, permission_id) values ( 1 , 1 );

insert into role_permission (role_id, permission_id) values ( 2 , 1 );

insert into role_permission (role_id, permission_id) values ( 2 , 2 );

项目结构

?

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

resources

|___application.yml

java

|___com

| |____gf

| | |____springbootjwtapplication.java

| | |____config

| | | |____.ds_store

| | | |____securityconfig.java

| | | |____myfiltersecurityinterceptor.java

| | | |____myinvocationsecuritymetadatasourceservice.java

| | | |____myaccessdecisionmanager.java

| | |____entity

| | | |____user.java

| | | |____rolepermisson.java

| | | |____role.java

| | |____mapper

| | | |____permissionmapper.java

| | | |____usermapper.java

| | | |____rolemapper.java

| | |____utils

| | | |____jwttokenutil.java

| | |____controller

| | | |____authcontroller.java

| | |____filter

| | | |____jwttokenfilter.java

| | |____service

| | | |____impl

| | | | |____authserviceimpl.java

| | | | |____userdetailsserviceimpl.java

| | | |____authservice.java

关键代码

pom.xml

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

<dependency>

  <groupid>org.springframework.boot</groupid>

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

</dependency>

<dependency>

  <groupid>org.springframework.boot</groupid>

  <artifactid>spring-boot-starter-security</artifactid>

</dependency>

<dependency>

  <groupid>io.jsonwebtoken</groupid>

  <artifactid>jjwt</artifactid>

  <version> 0.9 . 0 </version>

</dependency>

<dependency>

  <groupid>mysql</groupid>

  <artifactid>mysql-connector-java</artifactid>

  <scope>runtime</scope>

</dependency>

<dependency>

  <groupid>org.mybatis.spring.boot</groupid>

  <artifactid>mybatis-spring-boot-starter</artifactid>

  <version> 2.0 . 0 </version>

</dependency>

application.yml

?

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

spring:

  datasource:

  driver- class -name: com.mysql.cj.jdbc.driver

  url: jdbc:mysql: //localhost:3306/spring-security-jwt?useunicode=true&characterencoding=utf-8&usessl=false

  username: root

  password: root

securityconfig

@configuration

@enablewebsecurity

public class securityconfig extends websecurityconfigureradapter {

  @autowired

  private userdetailsservice userdetailsservice;

  @autowired

  public void configureglobal(authenticationmanagerbuilder auth) throws exception {

  //校验用户

  auth.userdetailsservice( userdetailsservice ).passwordencoder( new passwordencoder() {

   //对密码进行加密

   @override

   public string encode(charsequence charsequence) {

   system.out.println(charsequence.tostring());

   return digestutils.md5digestashex(charsequence.tostring().getbytes());

   }

   //对密码进行判断匹配

   @override

   public boolean matches(charsequence charsequence, string s) {

   string encode = digestutils.md5digestashex(charsequence.tostring().getbytes());

   boolean res = s.equals( encode );

   return res;

   }

  } );

  }

  @override

  protected void configure(httpsecurity http) throws exception {

  http.csrf().disable()

   //因为使用jwt,所以不需要httpsession

   .sessionmanagement().sessioncreationpolicy( sessioncreationpolicy.stateless).and()

   .authorizerequests()

   //options请求全部放行

   .antmatchers( httpmethod.options, "/**" ).permitall()

   //登录接口放行

   .antmatchers( "/auth/login" ).permitall()

   //其他接口全部接受验证

   .anyrequest().authenticated();

  //使用自定义的 token过滤器 验证请求的token是否合法

  http.addfilterbefore(authenticationtokenfilterbean(), usernamepasswordauthenticationfilter. class );

  http.headers().cachecontrol();

  }

  @bean

  public jwttokenfilter authenticationtokenfilterbean() throws exception {

  return new jwttokenfilter();

  }

  @bean

  @override

  public authenticationmanager authenticationmanagerbean() throws exception {

  return super .authenticationmanagerbean();

  }

}

jwttokenutil

?

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

/**

  * jwt 工具类

  */

@component

public class jwttokenutil implements serializable {

  private static final string claim_key_username = "sub" ;

  /**

  * 5天(毫秒)

  */

  private static final long expiration_time = 432000000 ;

  /**

  * jwt密码

  */

  private static final string secret = "secret" ;

  /**

  * 签发jwt

  */

  public string generatetoken(userdetails userdetails) {

  map<string, object> claims = new hashmap<>( 16 );

  claims.put( claim_key_username, userdetails.getusername() );

  return jwts.builder()

   .setclaims( claims )

   .setexpiration( new date( instant.now().toepochmilli() + expiration_time ) )

   .signwith( signaturealgorithm.hs512, secret )

   测试数据pact();

  }

  /**

  * 验证jwt

  */

  public boolean validatetoken(string token, userdetails userdetails) {

  user user = (user) userdetails;

  string username = getusernamefromtoken( token );

  return (username.equals( user.getusername() ) && !istokenexpired( token ));

  }

  /**

  * 获取token是否过期

  */

  public boolean istokenexpired(string token) {

  date expiration = getexpirationdatefromtoken( token );

  return expiration.before( new date() );

  }

  /**

  * 根据token获取username

  */

  public string getusernamefromtoken(string token) {

  string username = getclaimsfromtoken( token ).getsubject();

  return username;

  }

  /**

  * 获取token的过期时间

  */

  public date getexpirationdatefromtoken(string token) {

  date expiration = getclaimsfromtoken( token ).getexpiration();

  return expiration;

  }

  /**

  * 解析jwt

  */

  private claims getclaimsfromtoken(string token) {

  claims claims = jwts.parser()

   .setsigningkey( secret )

   .parseclaimsjws( token )

   .getbody();

  return claims;

  }

}

jwttokenfilter

?

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

@component

public class jwttokenfilter extends onceperrequestfilter {

  @autowired

  private userdetailsservice userdetailsservice;

  @autowired

  private jwttokenutil jwttokenutil;

  /**

  * 存放token的header key

  */

  public static final string header_string = "authorization" ;

  @override

  protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain chain) throws servletexception, ioexception {

  string token = request.getheader( header_string );

  if ( null != token) {

   string username = jwttokenutil.getusernamefromtoken(token);

   if (username != null && securitycontextholder.getcontext().getauthentication() == null ) {

   userdetails userdetails = this .userdetailsservice.loaduserbyusername(username);

   if (jwttokenutil.validatetoken(token, userdetails)) {

    usernamepasswordauthenticationtoken authentication = new usernamepasswordauthenticationtoken(

     userdetails, null , userdetails.getauthorities());

    authentication.setdetails( new webauthenticationdetailssource().builddetails(

     request));

    securitycontextholder.getcontext().setauthentication(authentication);

   }

   }

  }

  chain.dofilter(request, response);

  }

}

authserviceimpl

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

@service

public class authserviceimpl implements authservice {

  @autowired

  private authenticationmanager authenticationmanager;

  @autowired

  private userdetailsservice userdetailsservice;

  @autowired

  private jwttokenutil jwttokenutil;

  @override

  public string login(string username, string password) {

  usernamepasswordauthenticationtoken uptoken = new usernamepasswordauthenticationtoken( username, password );

  authentication authentication = authenticationmanager.authenticate(uptoken);

  securitycontextholder.getcontext().setauthentication(authentication);

  userdetails userdetails = userdetailsservice.loaduserbyusername( username );

  string token = jwttokenutil.generatetoken(userdetails);

  return token;

  }

}

关键代码就是这些,其他类代码参照后面提供的源码地址。

验证

登录,获取token

curl -X POST -d "username=admin&password=123456" http://127.0.0.1:8080/auth/login

返回

eyjhbgcioijiuzuxmij9.eyjzdwiioijhzg1pbiisimv4cci6mtu1ndq1mzuwmx0.sglveqndgul9ph1op3lh9xrdzjis42vkbapd2npjt7e1tkhcey7aufixnzg9vc885_jtq4-h8r6yctrrjzl8fq

不带token访问资源

curl -X POST -d "name=zhangsan" http://127.0.0.1:8080/admin/hi

返回,拒绝访问

?

1

2

3

4

5

6

7

{

  "timestamp" : "2019-03-31t08:50:55.894+0000" ,

  "status" : 403 ,

  "error" : "forbidden" ,

  "message" : "access denied" ,

  "path" : "/auth/login"

}

携带token访问资源

?

1

curl -x post -h "authorization: eyjhbgcioijiuzuxmij9.eyjzdwiioijhzg1pbiisimv4cci6mtu1ndq1mzuwmx0.sglveqndgul9ph1op3lh9xrdzjis42vkbapd2npjt7e1tkhcey7aufixnzg9vc885_jtq4-h8r6yctrrjzl8fq" -d "name=zhangsan" http: //127.0.0.1:8080/admin/hi

返回正确

?

1

hi zhangsan , you have 'admin' role

源码

https://github测试数据/gf-huanchupk/springbootlearning/tree/master/springboot-jwt

总结

以上所述是小编给大家介绍的spring boot security 结合 jwt 实现无状态的分布式api接口,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!

查看更多关于Spring Boot Security 结合 JWT 实现无状态的分布式API接口的详细内容...

  阅读:11次