springboot整合shiro和JWT 使用指北

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
密钥管理服务KMS,1000个密钥,100个凭据,1个月
简介: shiro JWT springboot


shiro是什么?

Shiro 是一个强大的简单易用的 Java 安全框架,主要用来更便捷的 认证,授权,加密,会话管理Shiro 首要的和最重要的目标就是容易使用并且容易理解,通过 Shiro 易于理解的API,您可以快速、轻松地获得任何应用程序——从最小的移动应用程序最大的网络和企业应用程序。

shiro的功能

  1. Authentication:身份认证、登录,验证用户是不是拥有相应的身份;
  2. Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行什么操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限!
  3. Session Manager:会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
  4. Cryptography:加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
  5. Web Support: Web支持,可以非常容易的集成到Web环境;
  6. Caching:缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
  7. Concurrency: Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传播过去Testing:提供测试支持;
  8. Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问;
  9. Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

JWT是什么?

JWT :JWT(JSON Web Token)是一种身份认证的方式,JWT 本质上就一段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。可以理解为对 token 这一技术提出一套规范,是在 RFC 7519 中提出的。

JWT的组成和优缺点

本文在这里仅简单介绍JWT的组成和优缺点。详细分析可以在相关搜索引擎中搜索。

JWT的组成

一个JWT token是一个字符串,它由头部、载荷、与签名中间有.分割组成;

头部(header):由令牌的类型和正在使用的签名算法组成;

载荷(playload):放置了token的一些基本信息,以帮助接收服务器理解,同时还有用户自定义信息,支持用户信息交换;

签名(Signature):主要是将前面的头部和载荷进行加密。

JWT的优缺点

JWT的优点:

  • 防CSRF:CSRF(Cross Site Request Forgery)跨站伪造请求。使用JWT登录成功后,会存放在本地local storage中;
  • 适合移动端:使用session需要服务端保存一份信息,使用token不需要只要请求携带token,还可跨语言使用;
  • 无状态:token中有身份验证的所有信息,增加了系统的可用性和伸缩性也带了同样的问题(在缺点中会介绍);
  • 单点登录友好:使用session需要我们把所有session信息存在一台电脑上,使用token的话token保存在客户端。

JWT的缺点:

  • 无状态导致在注销、退出登录、修改密码、修改角色||权限等场景下token依旧有效;
  • token续签问题:即token过期后如何动态刷新token。

shiro + JWT 使用

JWTtoken 令牌

`import org.apache.shiro.authc.AuthenticationToken;

public class JwtToken implementsAuthenticationToken{

private String token;

public JwtToken(String jwt){

this.token= jwt;

 }

 @Override

public Object getCredentials() {

return token;

 }

 @Override

public Object getPrincipal() {

return token;

 }  

}`

jwtutils JWT工具类主要是生成token设置过期时间

`import java.util.Date;

import org.springframework.boot.context.properties.ConfigurationProperties;

import org.springframework.stereotype.Component;

import io.jsonwebtoken.Claims;

import io.jsonwebtoken.Jwts;

import io.jsonwebtoken.SignatureAlgorithm;

import lombok.Data;

import lombok.extern.slf4j.Slf4j;

@Slf4j

@Data

@Component

@ConfigurationProperties(prefix = "blogspringback.jwt")

public class JwtUtils {

privatestatic String secret;

privatelong expire;

private String header;


/**

* @description: 生成token

  *** @param{long}userId

  *** @return{JWT token}

* @Author: 丑牛

*/

public String generateToken(longuserId){

// 过期时间

   Date dateNow =new Date();

   Date dateExpire =  new Date(dateNow.getTime() + expire * 1000);

return Jwts.builder()

         .setHeaderParam("type", "JWT")

         .setSubject(userId + "")

         .setIssuedAt(dateNow)

         .setExpiration(dateExpire)

         .signWith(SignatureAlgorithm.HS512, secret)

         .compact();

 }

publicstatic Claims getClaimsByToken(String token){

try {

return Jwts.parser()

           .setSigningKey(secret)

           .parseClaimsJws(token)

           .getBody();

   } catch (Exception e) {

     log.debug("token error", e);

return null;

   }

 }

/**

* @description: 判断token是否过期

  *** @param{Date}expeiration

  *** @returntrue:过期;false:未过期

* @Author: 丑牛

*/

publicboolean isTokenExpired(Date expeiration){

return expeiration.before(new Date());

 }

}`

JWTFilter 拦截请求

`@Component

public class Jwtfilter extendsAuthenticatingFilter {

 @Autowired

 JwtUtils jwtUtils;

 @Override

protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse serverResponse) throws Exception {

   HttpServletRequest request = (HttpServletRequest) servletRequest;

   String jwt = request.getHeader("Authorization");

if (ObjectUtils.isEmpty(jwt)){

return null;

   }

returnnew JwtToken(jwt);

 }

 @Override

protectedboolean onAccessDenied(ServletRequest servletRequest, ServletResponse serverResponse) throws Exception {

   HttpServletRequest request = (HttpServletRequest) servletRequest;

   String jwt = request.getHeader("Authorization");

if (ObjectUtils.isEmpty(jwt)) {

return true;

   } else {

     Claims claim = JwtUtils.getClaimsByToken(jwt);

if (claim == null || jwtUtils.isTokenExpired(claim.getExpiration())) {

thrownew ExpiredCredentialsException("token失效,请重新登录");

     }

return executeLogin(servletRequest, serverResponse);

   }

 }

 @Override

protectedboolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response){

   HttpServletResponse httpServletResponse = (HttpServletResponse) response;

   Throwable throwable = e.getCause() == null ? e : e.getCause();

   Result result = Result.fail(throwable.getMessage());

   String json = JSONUtil.toJsonStr(result);

try {

     httpServletResponse.getWriter().print(json);

   } catch (IOException ioException) {

   }

return false;

 }


 @Override

protectedboolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

   HttpServletRequest httpServletRequest = WebUtils.toHttp(request);

   HttpServletResponse httpServletResponse = WebUtils.toHttp(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"));

// 跨域时会首先发送一个OPTIONS请求,这里我们给OPTIONS请求直接返回正常状态

if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {

     httpServletResponse.setStatus(org.springframework.http.HttpStatus.OK.value());

return false;

   }

returnsuper.preHandle(request, response);

 }

}`

shiro config 设置自定义的filter

`@Configuration

public class ShiroConfig {

 @Autowired

 Jwtfilter jwtFilter;

 @Bean

public SessionManager sessionManager(RedisSessionDAO redisSessionDAO) {

   DefaultSessionManager sessionManager =new DefaultSessionManager();

   sessionManager.setSessionDAO(redisSessionDAO);

return sessionManager;

 }

 @Bean

public DefaultWebSecurityManager securityManager(AccountRealm accountRealm, SessionManager sessionManager,

     RedisCacheManager redisCacheManager) {

   DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager(accountRealm);

// inject sessionManager

   securityManager.setSessionManager(sessionManager);

// inject redisCacheManager

   securityManager.setCacheManager(redisCacheManager);

return securityManager;

 }

 @Bean

public ShiroFilterChainDefinition shiroFilterChainDefinition() {

   DefaultShiroFilterChainDefinition chainDefinition =new DefaultShiroFilterChainDefinition();

   Map<String, String> filterMap =new LinkedHashMap<>();

   filterMap.put("/**", "jwt");

   chainDefinition.addPathDefinitions(filterMap);

return chainDefinition;

 }

 @Bean("shiroFilterFactoryBean")

public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager,

                            ShiroFilterChainDefinition shiroFilterChainDefinition) {

   ShiroFilterFactoryBean shiroFilter =new ShiroFilterFactoryBean();

   shiroFilter.setSecurityManager(securityManager);

   Map<String, Filter> filters =new HashMap<>();

   filters.put("jwt", jwtFilter);

   shiroFilter.setFilters(filters);

   Map<String, String> filterMap = shiroFilterChainDefinition.getFilterChainMap();

   shiroFilter.setFilterChainDefinitionMap(filterMap);

return shiroFilter;

 }

}`

shiro工具类 获取当前用户登录信息

`public class ShiroUtil {

publicstatic AccountProfile getProfile(){

return (AccountProfile) SecurityUtils.getSubject().getPrincipal();

 }

}`

realm类 实现认证与授权

`@Component

public class AccountRealm extendsAuthorizingRealm{

 @Autowired

 JwtUtils jwtUtils;

 @Autowired

 UserService userService;

 @Override

publicboolean supports(AuthenticationToken token) {

return token instanceof JwtToken;

 }

 @Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

return null;

 }

 @Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

   JwtToken jwtToken = (JwtToken) token;

   String userId = JwtUtils.getClaimsByToken((String) jwtToken.getPrincipal()).getSubject();

   User user = userService.getById(Long.valueOf(userId));

if (user == null) {

thrownew UnknownAccountException("账户不存在");

   }

if (user.getStatus() ==-1) {

thrownew LockedAccountException("账户已被锁定");

   }

   AccountProfile profile =new AccountProfile();

   BeanUtil.copyProperties(user, profile);

returnnew SimpleAuthenticationInfo(profile, jwtToken.getCredentials(), getName());

 }


}`


PS 项目git地址https://github.com/hugfeature/spring-example/tree/main/blog-spring-back

相关文章
|
2月前
|
SQL Java 测试技术
在Spring boot中 使用JWT和过滤器实现登录认证
在Spring boot中 使用JWT和过滤器实现登录认证
178 0
|
1月前
|
安全 Java Apache
SpringBoot+Shiro(一)
SpringBoot+Shiro(一)
|
10天前
|
安全 Java 数据库
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
这篇文章是关于Apache Shiro权限管理框架的详细学习指南,涵盖了Shiro的基本概念、认证与授权流程,并通过Spring Boot测试模块演示了Shiro在单应用环境下的使用,包括与IniRealm、JdbcRealm的集成以及自定义Realm的实现。
26 3
shiro学习一:了解shiro,学习执行shiro的流程。使用springboot的测试模块学习shiro单应用(demo 6个)
|
17天前
|
Java API Apache
Springboot+shiro,完整教程,带你学会shiro
这篇文章提供了一个完整的Apache Shiro与Spring Boot结合使用的教程,包括Shiro的配置、使用以及在非Web和Web环境中进行身份验证和授权的示例。
33 2
Springboot+shiro,完整教程,带你学会shiro
|
17天前
|
前端开发 Java Apache
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
本文详细讲解了如何整合Apache Shiro与Spring Boot项目,包括数据库准备、项目配置、实体类、Mapper、Service、Controller的创建和配置,以及Shiro的配置和使用。
136 1
Springboot整合shiro,带你学会shiro,入门级别教程,由浅入深,完整代码案例,各位项目想加这个模块的人也可以看这个,又或者不会mybatis-plus的也可以看这个
|
11天前
|
JSON NoSQL Java
springBoot:jwt&redis&文件操作&常见请求错误代码&参数注解 (九)
该文档涵盖JWT(JSON Web Token)的组成、依赖、工具类创建及拦截器配置,并介绍了Redis的依赖配置与文件操作相关功能,包括文件上传、下载、删除及批量删除的方法。同时,文档还列举了常见的HTTP请求错误代码及其含义,并详细解释了@RequestParam与@PathVariable等参数注解的区别与用法。
|
10天前
|
NoSQL Java Redis
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
这篇文章介绍了如何使用Spring Boot整合Apache Shiro框架进行后端开发,包括认证和授权流程,并使用Redis存储Token以及MD5加密用户密码。
16 0
shiro学习四:使用springboot整合shiro,正常的企业级后端开发shiro认证鉴权流程。使用redis做token的过滤。md5做密码的加密。
|
18天前
|
存储 JSON 算法
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
文章介绍了JWT令牌的基础教程,包括其应用场景、组成部分、生成和校验方法,并在Springboot中使用JWT技术体系完成拦截器的实现。
32 0
JWT令牌基础教程 全方位带你剖析JWT令牌,在Springboot中使用JWT技术体系,完成拦截器的实现 Interceptor (后附源码)
|
5月前
|
安全 数据安全/隐私保护
Springboot+Spring security +jwt认证+动态授权
Springboot+Spring security +jwt认证+动态授权
196 0
|
3月前
|
JSON 安全 Java
使用Spring Boot和JWT实现用户认证
使用Spring Boot和JWT实现用户认证