springboot项目中使用shiro 自定义过滤器和token的方式

实现步骤主要是以下几步:

1. 在项目中导入maven依赖

     <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency>

2. shiro的核心配置类和代码

  1. 自定义(令牌实体)token
package com.ratel.fast.modules.sys.oauth2;
import org.apache.shiro.authc.AuthenticationToken;
/*** token*/
public class OAuth2Token implements AuthenticationToken {private String token;public OAuth2Token(String token){this.token = token;}@Overridepublic String getPrincipal() {return token;}@Overridepublic Object getCredentials() {return token;}
}
  1. token的生成工具
package com.ratel.fast.modules.sys.oauth2;
import com.ratel.fast.common.exception.RRException;
import java.security.MessageDigest;
import java.util.UUID;
/*** 生成token*/
public class TokenGenerator {public static String generateValue() {return generateValue(UUID.randomUUID().toString());}private static final char[] hexCode = "0123456789abcdef".toCharArray();public static String toHexString(byte[] data) {if(data == null) {return null;}StringBuilder r = new StringBuilder(data.length*2);for ( byte b : data) {r.append(hexCode[(b >> 4) & 0xF]);r.append(hexCode[(b & 0xF)]);}return r.toString();}public static String generateValue(String param) {try {MessageDigest algorithm = MessageDigest.getInstance("MD5");algorithm.reset();algorithm.update(param.getBytes());byte[] messageDigest = algorithm.digest();return toHexString(messageDigest);} catch (Exception e) {throw new RRException("生成Token失败", e);}}
}

3. 自定义的认证源

package com.ratel.fast.modules.sys.oauth2;
import com.ratel.fast.modules.sys.service.ShiroService;
import com.ratel.fast.modules.sys.entity.SysUserEntity;
import com.ratel.fast.modules.sys.entity.SysUserTokenEntity;
import com.ratel.fast.modules.sys.service.ShiroService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Set;/*** 认证***/
@Component
public class OAuth2Realm extends AuthorizingRealm {@Autowiredprivate ShiroService shiroService;@Overridepublic boolean supports(AuthenticationToken token) {return token instanceof OAuth2Token;}/*** 授权(验证权限时调用)* 前端在请求带@RequiresPermissions注解 注解的方法时会调用 doGetAuthorizationInfo 这个方法*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal();Long userId = user.getUserId();//用户权限列表Set<String> permsSet = shiroService.getUserPermissions(userId);SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();info.setStringPermissions(permsSet);return info;}/*** 认证(登录时调用)* 每次请求的时候都会调用这个方法验证token是否失效和用户是否被锁定*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String accessToken = (String) token.getPrincipal();//根据accessToken,查询用户信息SysUserTokenEntity tokenEntity = shiroService.queryByToken(accessToken);//token失效if(tokenEntity == null || tokenEntity.getExpireTime().getTime() < System.currentTimeMillis()){throw new IncorrectCredentialsException("token失效,请重新登录");}//查询用户信息SysUserEntity user = shiroService.queryUser(tokenEntity.getUserId());//账号锁定if(user.getStatus() == 0){throw new LockedAccountException("账号已被锁定,请联系管理员");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());return info;}
}

4. 自定义的过滤器

package com.ratel.fast.modules.sys.oauth2;import com.google.gson.Gson;
import com.ratel.fast.common.utils.HttpContextUtils;
import com.ratel.fast.common.utils.R;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.web.bind.annotation.RequestMethod;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** oauth2过滤器***/
public class OAuth2Filter extends AuthenticatingFilter {@Overrideprotected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {//获取请求tokenString token = getRequestToken((HttpServletRequest) request);if(StringUtils.isBlank(token)){return null;}return new OAuth2Token(token);}/*** 判断用户是否已经登录,*      如果是options的请求则放行,否则进行调用onAccessDenied进行token认证流程* @param request* @param response* @param mappedValue* @return*/@Overrideprotected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){return true;}return false;}@Overrideprotected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {//获取请求token,如果token不存在,直接返回401String token = getRequestToken((HttpServletRequest) request);if(StringUtils.isBlank(token)){HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));httpResponse.getWriter().print(json);return false;}return executeLogin(request, response);}@Overrideprotected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {HttpServletResponse httpResponse = (HttpServletResponse) response;httpResponse.setContentType("application/json;charset=utf-8");httpResponse.setHeader("Access-Control-Allow-Credentials", "true");httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin());try {//处理登录失败的异常Throwable throwable = e.getCause() == null ? e : e.getCause();R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());String json = new Gson().toJson(r);httpResponse.getWriter().print(json);} catch (IOException e1) {}return false;}/*** 获取请求的token*/private String getRequestToken(HttpServletRequest httpRequest){//从header中获取tokenString token = httpRequest.getHeader("token");//如果header中不存在token,则从参数中获取tokenif(StringUtils.isBlank(token)){token = httpRequest.getParameter("token");}return token;}}

5. 核心配置类

package com.ratel.fast.config;import com.ratel.fast.modules.sys.oauth2.OAuth2Filter;
import com.ratel.fast.modules.sys.oauth2.OAuth2Realm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;/*** Shiro配置***/
@Configuration
public class ShiroConfig {@Bean("securityManager")public SecurityManager securityManager(OAuth2Realm oAuth2Realm) {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(oAuth2Realm);securityManager.setRememberMeManager(null);return securityManager;}/*** 这个bean的名字必须叫 shiroFilter ,否则启动的时候会报错*  @Bean ("shiroFilter") 之后的括号可以不用写,spring默认方法名为的bean的名字* @param securityManager* @return*/@Bean("shiroFilter")public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);//oauth过滤Map<String, Filter> filters = new HashMap<>();//添加自定义过滤器filters.put("oauth2", new OAuth2Filter());shiroFilter.setFilters(filters);Map<String, String> filterMap = new LinkedHashMap<>();filterMap.put("/webjars/**", "anon");filterMap.put("/druid/**", "anon");filterMap.put("/app/**", "anon");filterMap.put("/sys/login", "anon");filterMap.put("/swagger/**", "anon");filterMap.put("/v2/api-docs", "anon");filterMap.put("/swagger-ui.html", "anon");filterMap.put("/swagger-resources/**", "anon");filterMap.put("/captcha.jpg", "anon");filterMap.put("/aaa.txt", "anon");//使用自定义过滤器拦截除上边以外的所有请求filterMap.put("/**", "oauth2");shiroFilter.setFilterChainDefinitionMap(filterMap);return shiroFilter;}@Bean("lifecycleBeanPostProcessor")public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();advisor.setSecurityManager(securityManager);return advisor;}}

6. 用户token类 (用于往数据库中存储的时候用)

package com.ratel.fast.modules.sys.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;/*** 系统用户Token***/
@Data
@TableName("sys_user_token")
public class SysUserTokenEntity implements Serializable {private static final long serialVersionUID = 1L;//用户ID@TableId(type = IdType.INPUT)private Long userId;//tokenprivate String token;//过期时间private Date expireTime;//更新时间private Date updateTime;}
  1. 用户登录使用的Controller
package com.ratel.fast.modules.sys.controller;import com.ratel.fast.common.utils.R;
import com.ratel.fast.modules.sys.entity.SysUserEntity;
import com.ratel.fast.modules.sys.form.SysLoginForm;
import com.ratel.fast.modules.sys.service.SysCaptchaService;
import com.ratel.fast.modules.sys.service.SysUserService;
import com.ratel.fast.modules.sys.service.SysUserTokenService;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;/*** 登录相关***/
@RestController
public class SysLoginController extends AbstractController {@Autowiredprivate SysUserService sysUserService;@Autowiredprivate SysUserTokenService sysUserTokenService;@Autowiredprivate SysCaptchaService sysCaptchaService;/*** 验证码*/@GetMapping("captcha.jpg")public void captcha(HttpServletResponse response, String uuid)throws IOException {response.setHeader("Cache-Control", "no-store, no-cache");response.setContentType("image/jpeg");//获取图片验证码BufferedImage image = sysCaptchaService.getCaptcha(uuid);ServletOutputStream out = response.getOutputStream();ImageIO.write(image, "jpg", out);IOUtils.closeQuietly(out);}/*** 登录*/@PostMapping("/sys/login")public Map<String, Object> login(@RequestBody SysLoginForm form)throws IOException {boolean captcha = sysCaptchaService.validate(form.getUuid(), form.getCaptcha());if(!captcha){return R.error("验证码不正确");}//用户信息SysUserEntity user = sysUserService.queryByUserName(form.getUsername());//账号不存在、密码错误if(user == null || !user.getPassword().equals(new Sha256Hash(form.getPassword(), user.getSalt()).toHex())) {return R.error("账号或密码不正确");}//账号锁定if(user.getStatus() == 0){return R.error("账号已被锁定,请联系管理员");}//生成token,并保存到数据库R r = sysUserTokenService.createToken(user.getUserId());return r;}/*** 退出*/@PostMapping("/sys/logout")public R logout() {sysUserTokenService.logout(getUserId());return R.ok();}}

到此我们已经完成了shiro的认证过程的代码。
记住一点,Shiro 不会去维护用户、维护权限;这些需要我们自己去设计 / 提供;然后通过相应的接口注入给 Shiro 即可。

shiro使用token登录流程

springboot项目中使用shiro 自定义过滤器和token的方式___shiro使用token登录流程相关推荐

  1. 关于springboot项目中两种自定义取值方案

    优先级:如果在项目中同时配置了.yml和.properties文件,那么会优先加载.properties文件. 作用:在properties中以.进行分割,.yml中以":"进行分 ...

  2. 在springboot项目中如何设计UrlFilter过滤器

    1.在我们Web项目中需要对非法Url和请求参数进行过滤拦截,所以就得配置一个UrlFilter过滤器,具体配置如下所示 一.创建UrlFilter配置类 import java.io.IOExcep ...

  3. springboot项目中利用@WebFilter注解和@Bean配置类两种方式实现Filter过滤器

    过滤器(Filter) 过滤器实际上就是对web资源进行拦截,做一些处理后再交给下一个过滤器或servlet处理.通常都是用来拦截request进行处理的,也可以对返回的response进行拦截处理 ...

  4. 根据自动装配原理在Springboot项目中自定义starter,并实现热插拔技术,自定义@enable

    根据自动装配原理在Springboot项目中自定义starter,并实现热插拔技术 自定义starter 简单步骤 使用测试 优化(热插拔技术) 自定义starter 简单步骤 创建项目,并引入aut ...

  5. 【SpringBoot项目中使用Mybatis批量插入百万条数据】

    SpringBoot项目中使用Mybatis批量插入百万条数据 话不多说,直接上代码,测试原生批处理的效率 开始测试 背景:因为一些业务问题,需要做多数据源,多库批量查询.插入操作,所以就研究了一下. ...

  6. SpringBoot项目中ModelMapper配置以及使用

    项目中对象与对象赋值转换使用的频率非常的高,比如数据库表实体对象(Entity)与业务类对象(Model)之间的赋值传递,或者模型对象(Model)与视图对象(ViewModel)之间的赋值传递.如果 ...

  7. SpringBoot项目中集成第三方登录功能

    SpringBoot项目中集成第三方登录功能 引言 1 环境准备 2 代码实现 3 第三方平台认证申请 4 打包和部署项目 5 第三方平台登录认证测试 6 参考文章 引言 最近想把自己在公众号上介绍过 ...

  8. 在SpringBoot项目中整合拦截器

    拦截器在Web系统中非常常见,对于某些全局统一的操作,我们可以把它提取到拦截器中实现.总结起来,拦截器大致有以下几种使用场景: 1.权限检查:如登录检测,进入处理程序检测用户是否登录,如果没有,则直接 ...

  9. springboot项目中接口防止恶意请求多次,重复请求的解决办法,适合小白

    在项目中,接口的暴露在外面,很多人就会恶意多次快速请求,那我们开发的接口和服务器在这样的频率下的话,服务器和数据库很快会奔溃的,那我们该怎么防止接口防刷呢?由于博主小白,很多都不懂,都是从网上一点一点 ...

最新文章

  1. 使用Pyecharts制作Bar3D用法详解
  2. 言有三文章 - AI系列完整阅读
  3. 配置 postCSS 自动添加 css 的兼容前缀||打包样式表中的图片和字体文件||打包处理 js 文件中的高级语法
  4. Apache Spark:更改架构之前必须解决的5个陷阱
  5. 【转】Eclipse+CDT+Gcc编译选项控制
  6. Kali Linux Network Scanning Cookbook读书笔记之nmap
  7. HAOI(十二省联考)2019 qwq记
  8. Atitit.数据检索与网络爬虫与数据采集的原理概论
  9. php强制时间,php如何强制转成字符串
  10. 算法探究:线性时间选择问题
  11. SaaSpace:25款最佳免费视频编辑软件工具
  12. CentOS服务器ntpdate同步及使用ntpdate同步时钟服务器时间
  13. 充电桩测试设备TK4860C交流充电桩检定装置
  14. Appium 手机 App 自动化代码说明_启动微信app
  15. 解决:kill 不掉进程
  16. linux系统下如何修改开机图片,Ubuntu Kylin下修改登录背景,用户头像,开机动画及自动更换壁纸...
  17. 鸿蒙系统2.0电视版,鸿蒙2.0系统,鸿蒙2.0发布系统官方最新版预约 v1.0-手游汇
  18. 飞桨分布式训练又推新品,4D混合并行可训千亿级AI模型
  19. 导航网站服务器,gps导航云服务器
  20. it之家鸿蒙手机系统,IT之家安卓版新功能!支持识别鸿蒙系统与鸿蒙应用,无需更新...

热门文章

  1. mysql proxy 主从_【MYSQL知识必知必会】MySQL主从复制读写分离(基于mysql-proxy实现)...
  2. [转载] spring mvc自定义int枚举转换器
  3. 标志寄存器_访问标志寄存器,并与寄存器B |交换标志寄存器F的内容 8085微处理器...
  4. Java LinkedHashMap clear()方法与示例
  5. np.expm1_JavaScript中带有示例的Math.expm1()方法
  6. arcgis字段计算器无法赋值_Arcgis空间连接工具的妙用
  7. Apache服务配置
  8. 计算机英语论文摘要,求英语高手翻译论文摘要,非常感谢!
  9. c++中cend end_vector :: cend()函数以及C ++ STL中的示例
  10. 如何保证 Redis 消息队列中的数据不丢失?