前言

这次在处理一个小项目时用到了前后端分离,服务端使用springboot2.x。权限验证使用了Shiro。前后端分离首先需要解决的是跨域问题,POST接口跨域时会预发送一个OPTIONS请求,浏览器收到响应后会继续执行POST请求。 前后端分离后为了保持会话状态使用session持久化插件shiro-redis,持久化session可以持久化到关系型数据库,也可以持久化到非关系型数据库(主要是重写SessionDao)。Shiro已提供了SessionDao接口和抽象类。如果项目中用到Swagger的话,还需要把swagger相关url放行。

搭建依赖

<dependency>
<!--session持久化插件-->
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.2.3</version>
</dependency>
<dependency>
<!--spring shiro依赖-->
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>

Shiro权限配置

1、ShiroConfig。这里主要是shiro核心配置。比如SecurityManager、SessionManager、CacheManager。

public class ShiroConfig {

    @Value("${spring.redis.shiro.host}")
private String host;
@Value("${spring.redis.shiro.port}")
private int port;
@Value("${spring.redis.shiro.timeout}")
private int timeout;
@Value("${spring.redis.shiro.password}")
private String password; /**
* 权限规则配置
**/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager); Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
filters.put("authc", new MyFormAuthorizationFilter()); Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //swagger资源不拦截
filterChainDefinitionMap.put("/swagger-ui.html", "anon");
filterChainDefinitionMap.put("/swagger-resources/**/**", "anon");
filterChainDefinitionMap.put("/v2/api-docs", "anon");
filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");
filterChainDefinitionMap.put("/configuration/security", "anon");
filterChainDefinitionMap.put("/configuration/ui", "anon"); filterChainDefinitionMap.put("/login/ajaxLogin", "anon");
filterChainDefinitionMap.put("/login/unauth", "anon");
filterChainDefinitionMap.put("/login/logout", "anon");
filterChainDefinitionMap.put("/login/register","anon");
filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setLoginUrl("/login/unauth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
} /**
* shiro安全管理器(权限验证核心配置)
**/
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
securityManager.setSessionManager(sessionManager());
securityManager.setCacheManager(cacheManager()); return securityManager;
} /**
* 会话管理
**/
@Bean
public SessionManager sessionManager() {
MySessionManager sessionManager = new MySessionManager();
sessionManager.setSessionIdUrlRewritingEnabled(false); //取消登陆跳转URL后面的jsessionid参数
sessionManager.setSessionDAO(sessionDAO());
sessionManager.setGlobalSessionTimeout(-1);//不过期
return sessionManager;
} /**
* 使用的是shiro-redis开源插件 缓存依赖
**/
@Bean
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host+":"+port);
redisManager.setTimeout(timeout);
redisManager.setPassword(password);
return redisManager;
} /**
* 使用的是shiro-redis开源插件 session持久化
**/
public RedisSessionDAO sessionDAO() {
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
} /**
* 缓存管理
**/
@Bean
public CacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
} /**
* 权限管理
**/
@Bean
public MyShiroRealm myShiroRealm() { return new MyShiroRealm();
}
}

2、MyShiroRealm 用户身份验证、自定义权限。

public class MyShiroRealm extends AuthorizingRealm {

    private Logger logger= LoggerFactory.getLogger(MyShiroRealm.class);

    @Resource
UserDao userDao; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("===================权限验证==================");
return null;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
User currentUser=userDao.findUser(token.getUsername());
if(null == currentUser){
throw new AuthenticationException("账户不存在");
} if(!currentUser.getPassword().equals(new String(token.getPassword()))){
throw new IncorrectCredentialsException("账户密码不正确");
} if(currentUser.getIsdel()==1){
throw new LockedAccountException("账户已冻结");
} Subject subject = SecurityUtils.getSubject(); BIUser biUser=new BIUser();
biUser.setUserId(currentUser.getUserId());
biUser.setOrgId(currentUser.getOrgid());
biUser.setUserName(currentUser.getUsername());
biUser.setPassword(currentUser.getPassword());
biUser.setSessionId(subject.getSession().getId().toString());
biUser.setIsdel(currentUser.getIsdel());
biUser.setCreateTime(currentUser.getCreatetime()); logger.info("======已授权"+biUser.toString()+"===="); return new SimpleAuthenticationInfo(biUser,biUser.getPassword(),biUser.getUserName());
}
}

3、MySessionManager。shiro权限验证是根据客户端Cookie中的JSESSIONID值来确定身份是否合格。前后端分离后这个地方需要处理。客户端调用服务端登陆接口,验证通过后返回给客户端一个token值(这里我放的是sessionid)。客户端保存token值,然后调用其他接口时把token值放在header中。对前端来说也就是放在ajax的headers参数中。

public class MySessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
} @Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//从前端ajax headers中获取这个参数用来判断授权
String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
if (StringUtils.hasLength(id)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
//从前端的cookie中取值
return super.getSessionId(request, response);
} }
}

4、MyFormAuthorizationFilter。对于跨域的POST请求,浏览器发起POST请求前都会发送一个OPTIONS请求已确定服务器是否可用,OPTIONS请求通过后继续执行POST请求,而shiro自带的权限验证是无法处理OPTIONS请求的,所以这里需要重写isAccessAllowed方法。

public class MyFormAuthorizationFilter extends FormAuthenticationFilter {
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) {
HttpServletRequest httpServletRequest = WebUtils.toHttp(servletRequest);
if ("OPTIONS".equals(httpServletRequest.getMethod())) {
return true;
}
return super.isAccessAllowed(servletRequest, servletResponse, o);
} }

5、处理跨域

 @Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("PUT", "DELETE", "GET", "POST")
.allowedHeaders("*")
.exposedHeaders("access-control-allow-headers", "access-control-allow-methods", "access-control-allow" +
"-origin", "access-control-max-age", "X-Frame-Options","Authorization")
.allowCredentials(false).maxAge(3600); }

在前后端分离项目中使用SpringBoot集成Shiro的更多相关文章

  1. 分享我在前后端分离项目中Gitlab-CI的经验

    长话短说,今天分享我为前后端分离项目搭建Gitlab CI/CD流程的一些额外经验. Before Gitlab-ci是Gitlab提供的CI/CD特性,结合Gitlab简单友好的配置界面,能愉悦的在 ...

  2. Spring-Gateway与Spring-Security在前后端分离项目中的实践

    前言 网上貌似webflux这一套的SpringSecurity操作资料貌似很少. 自己研究了一波,记录下来做一点备忘,如果能帮到也在迷惑的人一点点,就更好了. 新项目是前后端分离的项目,前台vue, ...

  3. 超简单!asp.net core前后端分离项目使用gitlab-ci持续集成到IIS

    现在好多使用gitlab-ci的持续集成的教程,大部分都是发布到linux系统上的,但是目前还是有很大一部分企业使用的都是windows系统使用IIS在部署.NET应用程序.这里写一下如何使用gitl ...

  4. 《论vue在前后端分离项目中的实践之年终总结》

    我是2014年的时候开始了解知道的vue,当时vue还不太成熟,想用但是又怕自己hold不住,况且那时候vue还没有成熟的(路由.验证.ui组件)插件,社区也是不温不火的,再说也没有合适的机遇让我去项 ...

  5. 服务器上详细前后端分离项目搭建(springboot+vue)

    介绍:本文用的经典的前后端分离开源项目ruoyi Gitee链接地址:https://gitee.com/y_project/RuoYi 一.拉取项目: 利用Git把项目拉取到本地,也可以直接利用id ...

  6. 前后端分离项目中后台集成shiro需要注意的二三事

    1. 修改 Shiro 认证失败后默认重定向处理问题 a. 继承需要使用的 ShiroFilter,重载 onAccessDenied() 方法: @Override protected boolea ...

  7. 08 Django REST Framework 解决前后端分离项目中的跨域问题

    01-安装模块 pip install django-cors-headers 02-添加到INSTALL_APPS中 INSTALLED_APPS = ( ... 'corsheaders', .. ...

  8. Aspnet Mvc 前后端分离项目手记(二)关于token认证

    在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习 ...

  9. docker-compose 部署 Vue+SpringBoot 前后端分离项目

    一.前言 本文将通过docker-compose来部署前端Vue项目到Nginx中,和运行后端SpringBoot项目 服务器基本环境: CentOS7.3 Dokcer MySQL 二.docker ...

随机推荐

  1. HDU1164_Eddy&#39;s research I【Miller Rabin素数测试】【Pollar Rho整数分解】

    Eddy's research I Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others ...

  2. Java--ConcurrentHashMap原理分析

    一.背景: 线程不安全的HashMap     因为多线程环境下,使用Hashmap进行put操作会引起死循环,导致CPU利用率接近100%,所以在并发情况下不能使用HashMap.   效率低下的H ...

  3. php+mysql+nginx于linux部署对环境

    始终linux在补锅匠,在尚未完成linux根据 - 型nginxserver环境进行部署,这些天来,无论它是什么部署,遇到的问题非常多,今天,我的环境中部署文档发行,够一起讨论一下,希望大家採用后遇 ...

  4. Leetcode 617 Merge Two Binary Trees 二叉树

    题意: 给定两棵树,将两棵树合并成一颗树 输入 Tree 1 Tree 2 1 2 / \ / \ 3 2 1 3 / \ \ 5 4 7 输出 合并的树 3 / \ 4 5 / \ \ 5 4 7 ...

  5. 使用带ParserContext参数的Xaml.Load方法

    原文:使用带ParserContext参数的Xaml.Load方法 如果一段XAML中存在一个标记需要从外部命名空间中解析, 就需要用到ParserContext类,  具体用法如下: Normal ...

  6. Linux下编译,要下载tar.xz,而不要下载zip,因为换行的编码不一样,对.h.cpp没有影响,但是对脚本有影响 good

    原因是 在win下编辑的时候,换行结尾是\n\r , 而在linux下 是\n,所以才会有 多出来的\r但是这个我是直接下载的官网文件解压的,没有动过啊. 破案了. linux下编译要下 .tar.x ...

  7. ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 中间件 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 中间件 上一章节中,我们我们有讲到 Startup 类中的 Confi ...

  8. Emgu-WPF 激光雷达研究-绘制雷达图

    原文:Emgu-WPF 激光雷达研究-绘制雷达图 硬件:Hokuyo URG04LX 环境:VS2017- win10- 64  Emgu_3.2.0.2682 语言:C#  WPF   数据解析参考 ...

  9. MVC CRUD 的两种方法

    //Index.cshtml @model IQueryable<MvcExam2.Models.Product>@{    Layout = null;}<!DOCTYPE htm ...

  10. wpf 复制/剪切到本地系统剪切板中以供右键粘贴用

    原文:wpf 复制/剪切到本地系统剪切板中以供右键粘贴用   http://www.cnblogs.com/yhdkzy/archive/2012/11/27/2790655.html   /// & ...