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

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登录流程

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

window.print 点击取消后再次打印无效_教程 | 图书馆自助复印打印机使用方法

图书馆自助复印打印机教程当当当&#xff01;教程君又来辽~今天为大家介绍的是咱们图书馆霸气十足的自助复印打印机实行全程无人化管理为读者提供自助打印、复印、扫描服务是你学习的小帮手&#xff01;实行全程无人化管理为读者提供自助打印、复印、扫描服务读者可在校园内任意…

android 支付模块封装,Android集成支付----支付宝支付总结与封装

前言类似于Android集成支付----微信支付总结与封装(可以查看本人另外一篇文章)&#xff0c;本文对支付宝支付进行一个总结与封装。相比于微信支付&#xff0c;支付宝支付没有那么多坑。集成支付宝支付SDK这里只是简要介绍&#xff0c;重点解说有坑的地方&#xff0c;具体参考官…

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

Springboot系列之Shiro、JWT、Redis 进行认证鉴权 Shiro架构 Apache Shiro是一个轻量级的安全框架 Shiro可以非常容易的开发出足够好的应用&#xff0c;其不仅可以用在JavaSE环境&#xff0c;也可以用在JavaEE环境。 Shiro可以帮助我们完成&#xff1a;认证、授权、加密、会…

kettle 先删除后插入_Kettle:使用触发器和快照表进行增量数据同步

主库为Oracle 11g数据库&#xff0c;针对需要同步的表建立增量数据临时表以及反向并通过水壶定时同步到PostgreSQL数据库。1&#xff0c;主库创建快照表和快照注意&#xff1a;快照表结构和源表结构必须一致&#xff01;--创建插入快照表 CREATE TABLE SPWUSER.WEB_CUSTOMER_IN…

SpringBoot中使用Shiro和JWT做认证和鉴权

最近新做的项目中使用了shiro和jwt来做简单的权限验证&#xff0c;在和springboot集成的过程中碰到了不少坑。做完之后对shiro的体系架构了解的也差不多了&#xff0c;现在把中间需要注意的点放出来&#xff0c;给大家做个参考。 相对于spring security来说&#xff0c;shiro出…

android 手机wifi重启,路由器要不要每天重启?多亏宽带师傅透露,难怪网速一天比一天慢!...

没WiFi&#xff0c;不成活&#xff01;手机和WiFi之间&#xff0c;就像空调加棉被&#xff0c;谁也离不开谁。图片来源于网络如今WiFi已经填满了我们生活的每个角落&#xff0c;很难想象&#xff0c;如果哪天没了网&#xff0c;世界会变成什么样&#xff01;可是家里的WiFi是越…

关于Apache Commons-Lang3的使用

关于Apache Commons-Lang3的使用 在日常工作中&#xff0c;我们经常要使用到一些开源工具包&#xff0c;比如String&#xff0c;Date等等。有时候我们并不清楚有这些工具类的存在&#xff0c;造成在开发过程中重新实现导致时间浪费&#xff0c;且开发的代码质量不佳。而apache…

linux 查看ip_如何在 Linux 中查看可用的网络接口 | Linux 中国

对于某些人来说&#xff0c;他们更偏爱在安装完系统后再进行网络的配置或者更改现存的设置。众所周知&#xff0c;为了在命令行中进行网络设定的配置&#xff0c;我们首先必须知道系统中有多少个可用的网络接口。-- Sk在我们安装完一个 Linux 系统后最为常见的任务便是网络配置…

实现springboot的starter

什么是 Spring Boot Spring Boot 基本上是 Spring 框架的扩展&#xff0c;它消除了设置 Spring 应用程序所需的复杂例行配置。我们在使用 Spring 框架的时候&#xff0c;我们接触得比较多的应该是 Spring MVC、 IOC 、 DI 、AOP 等等&#xff0c;而这些框架在使用的过程中会需…

android多点触摸手势,安卓手势学习笔记(三) 多点触控

8种机械键盘轴体对比本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f;跟踪多个触点当多个手指同时触碰屏幕时&#xff0c;系统产生如下的事件&#xff1a;ACTION_DOWN –第一个触点。它启动了手势&#xff0c;在MotionEvent中该触点的…

SpringBoot 自动配置实现流程

&#xff08;1&#xff09;SpringBoot启动的时候加载主配置类&#xff0c;开启了自动配置功能EnableAutoConfiguration。查看SpringBootApplication &#xff08;2&#xff09;查看EnableAutoConfiguration&#xff0c;其作用是利用AutoConfigurationImportSelector给容器中导入…

spring.factories 的妙用

现象 在阅读 Spring-Boot 相关源码时&#xff0c;常常见到 spring.factories 文件&#xff0c;里面写了自动配置&#xff08;AutoConfiguration&#xff09;相关的类名&#xff0c;因此产生了一个疑问&#xff1a;“明明自动配置的类已经打上了 Configuration 的注解&#xff0…

学习android 画板源代码,Android实现画画板案例

郑州app开发画画板案例。布局代码是三个button和一个imagesview下面是图片。布局代码就不展示了。下面是java代码。package cn.xhhkj.image;import androidx.appcompat.app.AppCompatActivity;import android.content.Intent;import android.graphics.Bitmap;import android.gr…

Spring Boot 之spring.factories

首先抛出一个问题&#xff1a;如果想要被Spring容器管理的Bean的路径不再Spring Boot 的包扫描路径下&#xff0c;怎么办呢&#xff1f;也就是如何去加载第三方的Bean 呢&#xff1f; 有两种方式可以解决&#xff1a; 这里我们使用Swagger的配置来做实验。 1&#xff1a;首先一…

dataearth可视域分析_谁不知道前期分析要用ARCGIS?我就是不会用啊

Q你有没有遇到这样的情况&#xff1f;身边的同事或同学经常说什么高程图、坡向图、坡度图、生态水文图、热力密度图、大数据算法&#xff0c;而自己却一无所知&#xff0c;心里感到特别的慌啊。文末附ArcGIS最新版及超强数据神器 很多人会说&#xff0c;“设计师是感性的&#…

@ImportResource()注解的使用

ImportResource注解用于导入Spring的配置文件&#xff0c;让配置文件里面的内容生效&#xff1b;(就是以前写的springmvc.xml、applicationContext.xml) Spring Boot里面没有Spring的配置文件&#xff0c;我们自己编写的配置文件&#xff0c;也不能自动识别&#xff1b; 想让Sp…

mybatis3 没有生成example_网站图片尺寸自动生成

目录1. 背景2. 实现思路2.1. 尺寸动态变化2.2. 实时裁剪并静态化3. web或代理服务器插件实现方案1. 背景某天我的前同事给我打电话&#xff0c;说他们的负载很高&#xff0c;经查发现网站首页有20M&#xff0c;原因是首页直接引用高清图片&#xff0c;没有安装分辨率生成缩图。…

无法删除所有指定的值_AutoCAD所有系统变量大全

ACADLSPASDOC 0 仅将 acad.lsp 加载到 AutoCAD 任务打开的第一个图形中; 1 将 acad.lsp 加载到每一个打开的图形中ACADPREFIX 存储由 ACAD 环境变量指定的目录路径(如果有的话)&#xff0c;如果需要则附加路径分隔符ACADVER 存储 AutoCAD 的版本号。这个变量与 DXF 文件标题变量…

简述HTML语言概念,HTML语言的基本概念和基本格式.doc

HTML语言的基本概念和基本格式HTML语言的基本概念和基本格式当使用Netscape Navigator 与 Interne Explorer 这些浏览器在Interent上尽情遨游的时候&#xff0c;肯定会被丰富多彩的屏幕内容所吸引&#xff0c;而这些丰富多彩的内容背后&#xff0c;只是用一种简单的超文本标志语…

为什么线程池里的方法会执行两次_新手一看就懂的线程池

作者:码农田小齐来源:https://www.cnblogs.com/nycsde/p/14003888.html那相信大家也能感受到&#xff0c;其实用多线程是很麻烦的&#xff0c;包括线程的创建、销毁和调度等等&#xff0c;而且我们平时工作时好像也并没有这样来 new 一个线程&#xff0c;其实是因为很多框架的底…