一般来讲,对于RESTful API都会有认证(Authentication)和授权(Authorization)过程,保证API的安全性。

Authentication指的是确定这个用户的身份,Authorization是确定该用户拥有什么操作权限。

认证方式一般有三种

Basic Authentication

这种方式是直接将用户名和密码放到Header中,使用Authorization: Basic Zm9vOmJhcg==,使用最简单但是最不安全。

TOKEN认证

这种方式也是再HTTP头中,使用Authorization: Bearer <token>,使用最广泛的TOKEN是JWT,通过签名过的TOKEN。

OAuth2.0

这种方式安全等级最高,但是也是最复杂的。如果不是大型API平台或者需要给第三方APP使用的,没必要整这么复杂。

一般项目中的RESTful API使用JWT来做认证就足够了。

关于JWT的介绍请参考我的另一篇文章:【安全贴士】JWT介绍和安全防范 | 飞污熊博客

SpringBoot集成JWT

简要的说明下我们为什么要用JWT,因为我们要实现完全的前后端分离,所以不可能使用session,cookie的方式进行鉴权, 所以JWT就被派上了用场,可以通过一个加密密钥来进行前后端的鉴权。

程序逻辑:

  1. 我们POST用户名与密码到/login进行登入,如果成功返回一个加密token,失败的话直接返回401错误。
  2. 之后用户访问每一个需要权限的网址请求必须在header中添加Authorization字段,例如Authorization: token,token为密钥。
  3. 后台会进行token的校验,如果不通过直接返回401。

这里我讲一下如何在SpringBoot中使用JWT来做接口权限认证,安全框架依旧使用Shiro,JWT的实现使用 jjwt

添加Maven依赖

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
<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-jetty</artifactId>
</dependency>
<dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.4.0</version>
</dependency>
<!-- shiro 权限控制 -->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>
<!-- shiro ehcache (shiro缓存)-->
<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.0</version><exclusions><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>

创建用户Service

这个在shiro一节讲过如果创建角色权限表,添加用户Service来执行查找用户操作,这里就不多讲具体实现了,只列出关键代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
/*** 通过名称查找用户** @param username* @return*/
public ManagerInfo findByUsername(String username) {ManagerInfo managerInfo = managerInfoDao.findByUsername(username);if (managerInfo == null) {throw new UnknownAccountException();}return managerInfo;
}

用户信息类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class ManagerInfo implements Serializable {private static final long serialVersionUID = 1L;/*** 主键ID*/private Integer id;/*** 账号*/private String username;/*** 密码*/private String password;/*** md5密码盐*/private String salt;/*** 一个管理员具有多个角色*/private List<SysRole> roles;

JWT工具类

我们写一个简单的JWT加密,校验工具,并且使用用户自己的密码充当加密密钥, 这样保证了token 即使被他人截获也无法破解。并且我们在token中附带了username信息,并且设置密钥5分钟就会过期。

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
public class JWTUtil {private static final Logger log = LoggerFactory.getLogger(JWTUtil.class);// 过期时间5分钟private static final long EXPIRE_TIME = 5 * 60 * 1000;/*** 生成签名,5min后过期** @param username 用户名* @param secret   用户的密码* @return 加密的token*/public static String sign(String username, String secret) {Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);Algorithm algorithm = Algorithm.HMAC256(secret);// 附带username信息return JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm);}/*** 校验token是否正确** @param token  密钥* @param secret 用户的密码* @return 是否正确*/public static boolean verify(String token, String username, String secret) {Algorithm algorithm = Algorithm.HMAC256(secret);JWTVerifier verifier = JWT.require(algorithm).withClaim("username", username).build();DecodedJWT jwt = verifier.verify(token);return true;}/*** 获得token中的信息无需secret解密也能获得** @return token中包含的用户名*/public static String getUsername(String token) {DecodedJWT jwt = JWT.decode(token);return jwt.getClaim("username").asString();}}

编写登录接口

为了让用户登录的时候获取到正确的JWT Token,需要实现登录接口,这里我编写一个LoginController.java

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
/*** 登录接口类*/
@RestController
public class LoginController {@Resourceprivate ManagerInfoService managerInfoService;private static final Logger _logger = LoggerFactory.getLogger(LoginController.class);@PostMapping("/login")public BaseResponse<String> login(@RequestHeader(name="Content-Type", defaultValue = "application/json") String contentType,@RequestBody LoginParam loginParam) {_logger.info("用户请求登录获取Token");String username = loginParam.getUsername();String password = loginParam.getPassword();ManagerInfo user = managerInfoService.findByUsername(username);//随机数盐String salt = user.getSalt();//原密码加密(通过username + salt作为盐)String encodedPassword = ShiroKit.md5(password, username + salt);if (user.getPassword().equals(encodedPassword)) {return new BaseResponse<>(true, "Login success", JWTUtil.sign(username, encodedPassword));} else {throw new UnauthorizedException();}}}

注意上面登录的时候,我会从数据库中把这个用户取出来,密码加盐算MD5值比较,通过之后再用密码作为密钥来签名生成JWT。

编写RESTful接口

先编写一个通用的接口返回类:

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
/*** API接口的基础返回类** @author XiongNeng* @version 1.0* @since 2018/1/7*/
public class BaseResponse<T> {/*** 是否成功*/private boolean success;/*** 说明*/private String msg;/*** 返回数据*/private T data;public BaseResponse() {}public BaseResponse(boolean success, String msg, T data) {this.success = success;this.msg = msg;this.data = data;}
}

通过SpringMVC实现RESTful接口,这里我只写一个示例方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*** 机具管理API接口类*/
@RestController
@RequestMapping(value = "/api/v1")
public class PublicController {private static final Logger _logger = LoggerFactory.getLogger(PublicController.class);/*** 入网绑定查询接口** @return 是否入网*/@RequestMapping(value = "/join", method = RequestMethod.GET)@RequiresAuthenticationpublic BaseResponse join(@RequestParam("imei") String imei) {_logger.info("入网查询接口 start... imei=" + imei);BaseResponse result = new BaseResponse();result.setSuccess(true);result.setMsg("已入网并绑定了网点");return result;}
}

自定义异常

为了实现我自己能够手动抛出异常,我自己写了一个UnauthorizedException.java

1
2
3
4
5
6
7
8
9
public class UnauthorizedException extends RuntimeException {public UnauthorizedException(String msg) {super(msg);}public UnauthorizedException() {super();}
}

处理框架异常

之前说过restful要统一返回的格式,所以我们也要全局处理Spring Boot的抛出异常。利用@RestControllerAdvice能很好的实现。 注意这个统一异常处理器只对认证过的用户调用接口中的异常有作用,对AuthenticationException没有用

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
@RestControllerAdvice
public class ExceptionController {// 捕捉shiro的异常@ResponseStatus(HttpStatus.UNAUTHORIZED)@ExceptionHandler(ShiroException.class)public BaseResponse handle401(ShiroException e) {return new BaseResponse(false, "shiro的异常", null);}// 捕捉UnauthorizedException@ResponseStatus(HttpStatus.UNAUTHORIZED)@ExceptionHandler(UnauthorizedException.class)public BaseResponse handle401() {return new BaseResponse(false, "UnauthorizedException", null);}// 捕捉其他所有异常@ExceptionHandler(Exception.class)@ResponseStatus(HttpStatus.BAD_REQUEST)public BaseResponse globalException(HttpServletRequest request, Throwable ex) {return new BaseResponse(false, "其他异常", null);}private HttpStatus getStatus(HttpServletRequest request) {Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");if (statusCode == null) {return HttpStatus.INTERNAL_SERVER_ERROR;}return HttpStatus.valueOf(statusCode);}
}

配置Shiro

大家可以先看下官方的 Spring-Shiro 整合教程,有个初步的了解。 不过既然我们用了SpringBoot,那我们肯定要争取零配置文件。

实现JWTToken

JWTToken差不多就是Shiro用户名密码的载体。因为我们是前后端分离,服务器无需保存用户状态,所以不需要RememberMe这类功能, 我们简单的实现下AuthenticationToken接口即可。因为token自己已经包含了用户名等信息,所以这里我就弄了一个字段。 如果你喜欢钻研,可以看看官方的UsernamePasswordToken是如何实现的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class JWTToken implements AuthenticationToken {// 密钥private String token;public JWTToken(String token) {this.token = token;}@Overridepublic Object getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;}
}

实现Realm

realm的用于处理用户是否合法的这一块,需要我们自己实现。

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
/*** Description  : 身份校验核心类*/public class MyShiroRealm extends AuthorizingRealm {private static final Logger _logger = LoggerFactory.getLogger(MyShiroRealm.class);@AutowiredManagerInfoService managerInfoService;/*** JWT签名密钥,这里没用。我使用的是用户的MD5密码作为签名密钥*/public static final String SECRET = "9281e268b77b7c439a20b46fd1483b9a";/*** 必须重写此方法,不然Shiro会报错*/@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof JWTToken;}/*** 认证信息(身份验证)* Authentication 是用来验证用户身份** @param auth* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth)throws AuthenticationException {_logger.info("MyShiroRealm.doGetAuthenticationInfo()");String token = (String) auth.getCredentials();// 解密获得username,用于和数据库进行对比String username = JWTUtil.getUsername(token);if (username == null) {throw new AuthenticationException("token invalid");}//通过username从数据库中查找 ManagerInfo对象//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法ManagerInfo managerInfo = managerInfoService.findByUsername(username);if (managerInfo == null) {throw new AuthenticationException("User didn't existed!");}if (!JWTUtil.verify(token, username, managerInfo.getPassword())) {throw new AuthenticationException("Username or password error");}return new SimpleAuthenticationInfo(token, token, "my_realm");}/*** 此方法调用hasRole,hasPermission的时候才会进行回调.* <p>* 权限信息.(授权):* 1、如果用户正常退出,缓存自动清空;* 2、如果用户非正常退出,缓存自动清空;* 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。* (需要手动编程进行实现;放在service进行调用)* 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例,调用clearCached方法;* :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。** @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {/** 当没有使用缓存的时候,不断刷新页面的话,这个代码会不断执行,* 当其实没有必要每次都重新设置权限信息,所以我们需要放到缓存中进行管理;* 当放到缓存中时,这样的话,doGetAuthorizationInfo就只会执行一次了,* 缓存过期之后会再次执行。*/_logger.info("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");String username = JWTUtil.getUsername(principals.toString());// 下面的可以使用缓存提升速度ManagerInfo managerInfo = managerInfoService.findByUsername(username);SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();//设置相应角色的权限信息for (SysRole role : managerInfo.getRoles()) {//设置角色authorizationInfo.addRole(role.getRole());for (Permission p : role.getPermissions()) {//设置权限authorizationInfo.addStringPermission(p.getPermission());}}return authorizationInfo;}}

doGetAuthenticationInfo中用户可以自定义抛出很多异常,详情见文档。

重写Filter

所有的请求都会先经过Filter,所以我们继承官方的BasicHttpAuthenticationFilter,并且重写鉴权的方法, 另外通过重写preHandle,实现跨越访问。

代码的执行流程preHandle->isAccessAllowed->isLoginAttempt->executeLogin

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
public class JWTFilter extends BasicHttpAuthenticationFilter {private Logger LOGGER = LoggerFactory.getLogger(this.getClass());/*** 判断用户是否想要登入。* 检测header里面是否包含Authorization字段即可*/@Overrideprotected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {HttpServletRequest req = (HttpServletRequest) request;String authorization = req.getHeader("Authorization");return authorization != null;}/****/@Overrideprotected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;String authorization = httpServletRequest.getHeader("Authorization");JWTToken token = new JWTToken(authorization);// 提交给realm进行登入,如果错误他会抛出异常并被捕获getSubject(request, response).login(token);// 如果没有抛出异常则代表登入成功,返回truereturn true;}/*** 这里我们详细说明下为什么最终返回的都是true,即允许访问* 例如我们提供一个地址 GET /article* 登入用户和游客看到的内容是不同的* 如果在这里返回了false,请求会被直接拦截,用户看不到任何东西* 所以我们在这里返回true,Controller中可以通过 subject.isAuthenticated() 来判断用户是否登入* 如果有些资源只有登入用户才能访问,我们只需要在方法上面加上 @RequiresAuthentication 注解即可* 但是这样做有一个缺点,就是不能够对GET,POST等请求进行分别过滤鉴权(因为我们重写了官方的方法),但实际上对应用影响不大*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {if (isLoginAttempt(request, response)) {try {executeLogin(request, response);} catch (Exception e) {response401(request, response);}}return true;}/*** 对跨域提供支持*/@Overrideprotected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {HttpServletRequest httpServletRequest = (HttpServletRequest) request;HttpServletResponse httpServletResponse = (HttpServletResponse) response;httpServletResponse.setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");httpServletResponse.setHeader("Access-Control-Allow-Headers", httpServletRequest.getHeader("Access-Control-Request-Headers"));// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {httpServletResponse.setStatus(HttpStatus.OK.value());return false;}return super.preHandle(request, response);}/*** 将非法请求跳转到 /401*/private void response401(ServletRequest req, ServletResponse resp) {try {HttpServletResponse httpServletResponse = (HttpServletResponse) resp;httpServletResponse.sendRedirect("/401");} catch (IOException e) {LOGGER.error(e.getMessage());}}
}

编写ShiroConfig配置类

这里我还增加了EhCache缓存管理支持,不需要每次都调用数据库做授权。

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
@Configuration
@Order(1)
public class ShiroConfig {/*** ShiroFilterFactoryBean 处理拦截资源文件问题。* 注意:单独一个ShiroFilterFactoryBean配置是或报错的,以为在* 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager Filter Chain定义说明* 1、一个URL可以配置多个Filter,使用逗号分隔* 2、当设置多个过滤器时,全部验证通过,才视为通过* 3、部分过滤器可指定参数,如perms,roles*/@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// 必须设置 SecurityManagershiroFilterFactoryBean.setSecurityManager(securityManager);//验证码过滤器Map<String, Filter> filtersMap = shiroFilterFactoryBean.getFilters();filtersMap.put("jwt", new JWTFilter());shiroFilterFactoryBean.setFilters(filtersMap);// 拦截器Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();// 其他的filterChainDefinitionMap.put("/**", "jwt");// 访问401和404页面不通过我们的FilterfilterChainDefinitionMap.put("/401", "anon");filterChainDefinitionMap.put("/404", "anon");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Beanpublic SecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realm.securityManager.setRealm(myShiroRealm());//注入缓存管理器securityManager.setCacheManager(ehCacheManager());// 关闭shiro自带的sessionDefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();defaultSessionStorageEvaluator.setSessionStorageEnabled(false);subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);securityManager.setSubjectDAO(subjectDAO);return securityManager;}/*** 身份认证realm; (这个需要自己写,账号密码校验;权限等)*/@Beanpublic MyShiroRealm myShiroRealm() {MyShiroRealm myShiroRealm = new MyShiroRealm();return myShiroRealm;}/*** 开启shiro aop注解支持. 使用代理方式; 所以需要开启代码支持;** @param securityManager 安全管理器* @return 授权Advisor*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}/*** shiro缓存管理器;* 需要注入对应的其它的实体类中:* 1、安全管理器:securityManager* 可见securityManager是整个shiro的核心;** @return*/@Beanpublic EhCacheManager ehCacheManager() {EhCacheManager cacheManager = new EhCacheManager();cacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");return cacheManager;}}

里面URL规则自己参考文档 Apache Shiro Web Support | Apache Shiro ,这个在shiro那篇说的很清楚了。

运行验证

最后是将代码跑起来验证这一切是否正常。

启动SpringBoot后,先通过POST请求登录拿到token

然后在调用入网接口的时候在header中带上这个token认证:

如果token认证不正确会报异常:

如果使用普通用户登录,认证正确但是授权访问接口失败,会返回如下的未授权结果:

【SpringBoot系列】最详细demo--集成JWT实现接口权限认证相关推荐

  1. SpringBoot 快速集成 JWT 实现用户登录认证

    前言:当今前后端分离时代,基于Token的会话保持机制比传统的Session/Cookie机制更加方便,下面我会介绍SpringBoot快速集成JWT库java-jwt以完成用户登录认证. 一.JWT ...

  2. springboot整合shiro + jwt + redis实现权限认证(上手即用)

    目录 前言 项目结构 依赖导入 建数据库表 建表语句 使用插件生成增删改查 添加MyRealm 添加ShiroConfig 添加JwtFilter JWT相关得类 JwtToken JwtAudien ...

  3. 数据权限过滤_带你实现SpringBoot整合JWT+Shiro进行权限认证「附源码地址」

    JWT JSON Web Token(JWT)是一个非常轻巧的规范.这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息. 我们利用一定的编码生成 Token,并在 Token 中加入一 ...

  4. jwt无状态权限认证(pings-shiro-jwt)

    单用户并发访问的问题 当用户AccessToken失效,用户使用该失效的AccessToken同时发起多个请求,会产生多AccessToken和RefreshToken认证失败问题: 多AccessT ...

  5. OpenLDAP + Ranger +Kerberos 三方集成实现身份、权限认证

    基于OpenLDAP和Kerberos权限认证,在Ranger配置权限策略,实现三方集成.身份认证和权限管理之间又会产生什么火花~,下面将我们一步步揭开它们的神秘面. 在我们搜索时,常见的两张关于三方 ...

  6. SpringBoot系列:Spring Boot集成定时任务Quartz

    一.关于Quartz Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用.在java企业级应用中,Q ...

  7. SpringBoot系列:Spring Boot集成定时任务Quartz,java百度云短信发送

    然后创建QuartzConfig,接着定义JobDetail,JobDetail由JobBuilder构建,同时关联了任务QuartzJob1. @Configuration public class ...

  8. 搭建认证服务器 - Spring Security Oauth2.0 集成 Jwt 之 【密码认证流程】 总结

    在搭建介绍流程之前,确保您已经搭建了一个 Eureka 注册中心,因为没有注册中心的话会报错(也有可能我搭建的认证服务器是我项目的一个子模块的原因):Request execution error. ...

  9. Springboot系列之Shiro、JWT、Redis 进行认证鉴权

    Springboot系列之Shiro.JWT.Redis 进行认证鉴权 Shiro架构 Apache Shiro是一个轻量级的安全框架 Shiro可以非常容易的开发出足够好的应用,其不仅可以用在Jav ...

  10. SpringBoot笔记:SpringBoot集成JWT实战

    文章目录 JWT 简介 概念 JWT 的认证流程 优缺点 JWT 消息构成 header playload signature SpringBoot 集成 JWT 实战 maven 依赖 JwtUti ...

最新文章

  1. 暗黑再临Java正版_暗黑破坏神之墨菲斯托
  2. ECharts的简单使用过程
  3. 设计模式C++实现(14)——职责链模式
  4. 3d000: no database selected_No.[C9]020
  5. ffmpeg添加到环境变量_Windows + MSVC环境编译ffmpeg
  6. Intent实现页面跳转
  7. I00004 贝尔三角形(解法二)
  8. Java工具集-数学(圆柱体,椎体工具类)
  9. ios标准时间转为北京时间
  10. 淘宝/天猫获取商品历史价格信息 API 返回值说明
  11. OllyDbg逆向破解入门攻略?拿来吧你!
  12. python随机函数random、画、星轨_如何使用 NVIDIA StyleGAN 生成自己的动漫(老婆)头像...
  13. python简单爬虫 指定汉字的笔画动图下载
  14. WordCounter.icu - 一个简单的在线实时字数统计工具
  15. oracle日记账查询,OraEBSR12GL日记账业务操作16:按汇总模板进行查询
  16. TEM一维正演matlab,瞬变电磁大定源回线一维正演模拟研究
  17. js实现父页面的刷新
  18. 在虚拟机VirtualBox7.0.6+openEuler20.03TSL上安装部署openGauss3.1.1数据库快速(一键)安装指导手册
  19. java geotools 坐标转换
  20. 内部招聘和外部招聘的优劣势分析

热门文章

  1. ue4蓝图运行顺序_UE4蓝图流程控制
  2. 2021-09-23各个国家简称以及收手机区号
  3. 数字频率系数测试软件,基于单片机简易数字频率计设计方案汇总
  4. Webservice接口-简单实例
  5. “小而美”走到十字路口,吉利或收购魅族助车机闭环
  6. 【无标题】drv8825步进电机驱动板子原理图
  7. 2021-07-10树莓派PWM控制三极管(S8050)实现风扇调速
  8. excel文件损坏修复绝招_修复数据工具大盘点,让你快速掌握电脑数据恢复的秘密武器...
  9. 【sketchup 2021】草图大师的辅助建模工具1【量角器与文字、尺寸标注与三维字、实体工具】
  10. matlab风玫瑰图,基于Matlab的风玫瑰图绘制