在前后端分离项目中使用SpringBoot集成Shiro
前言
这次在处理一个小项目时用到了前后端分离,服务端使用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的更多相关文章
- 分享我在前后端分离项目中Gitlab-CI的经验
长话短说,今天分享我为前后端分离项目搭建Gitlab CI/CD流程的一些额外经验. Before Gitlab-ci是Gitlab提供的CI/CD特性,结合Gitlab简单友好的配置界面,能愉悦的在 ...
- Spring-Gateway与Spring-Security在前后端分离项目中的实践
前言 网上貌似webflux这一套的SpringSecurity操作资料貌似很少. 自己研究了一波,记录下来做一点备忘,如果能帮到也在迷惑的人一点点,就更好了. 新项目是前后端分离的项目,前台vue, ...
- 超简单!asp.net core前后端分离项目使用gitlab-ci持续集成到IIS
现在好多使用gitlab-ci的持续集成的教程,大部分都是发布到linux系统上的,但是目前还是有很大一部分企业使用的都是windows系统使用IIS在部署.NET应用程序.这里写一下如何使用gitl ...
- 《论vue在前后端分离项目中的实践之年终总结》
我是2014年的时候开始了解知道的vue,当时vue还不太成熟,想用但是又怕自己hold不住,况且那时候vue还没有成熟的(路由.验证.ui组件)插件,社区也是不温不火的,再说也没有合适的机遇让我去项 ...
- 服务器上详细前后端分离项目搭建(springboot+vue)
介绍:本文用的经典的前后端分离开源项目ruoyi Gitee链接地址:https://gitee.com/y_project/RuoYi 一.拉取项目: 利用Git把项目拉取到本地,也可以直接利用id ...
- 前后端分离项目中后台集成shiro需要注意的二三事
1. 修改 Shiro 认证失败后默认重定向处理问题 a. 继承需要使用的 ShiroFilter,重载 onAccessDenied() 方法: @Override protected boolea ...
- 08 Django REST Framework 解决前后端分离项目中的跨域问题
01-安装模块 pip install django-cors-headers 02-添加到INSTALL_APPS中 INSTALLED_APPS = ( ... 'corsheaders', .. ...
- Aspnet Mvc 前后端分离项目手记(二)关于token认证
在前后端分离的项目中,首先我们要解决的问题就是身份认证 以往的时候,我们使用cookie+session,或者只用cookie来保持会话. 一,先来复习一下cookie和session 首先我们来复习 ...
- docker-compose 部署 Vue+SpringBoot 前后端分离项目
一.前言 本文将通过docker-compose来部署前端Vue项目到Nginx中,和运行后端SpringBoot项目 服务器基本环境: CentOS7.3 Dokcer MySQL 二.docker ...
随机推荐
- Using default security password
不展示Using default security password的解决办法: import org.springframework.context.annotation.Bean; import ...
- <模拟电子学习1>Multisim 12.0 结构和仿真51最小的单芯片系统
周围环境: 系统环境: win7 64位置 软件平台:Multisim 12.0 目的: 刚毕业,可是模电知识也忘得差点儿相同了,加之自己想搞搞硬件设计.假设仅仅是看模电书.不实践,还是终觉浅.当做兴 ...
- VS2010设置VC6的字体样式及背景色、选中字高亮
习惯了VC6.0的fixedsys字体,用VS2010还真不习惯.把VS2010打造成经典的.熟悉的模样,也并非难事.网上有相应的文章,我再记录下来,主要是为了自己查找方便(刚刚重装了系统,一切从头再 ...
- Cocos2dx 温馨提示(十)三种缓存类演示
在介绍了三级缓存,首先,我们必须弄清楚的一个问题:什么是纹理?它的通俗的解释,质感是它的含义的图片. 当图像加载到内存中后,,它是存在于纹理的形式.由于从这里可以看出.纹理事实上就是一块内存.这块内存 ...
- [实时更新]jquery完整版下载
jquery-2.1.0 注!不再支持IE 6/7/8 直接引用地址: 开发版地址1: <script src="http://code.jquery.com/jquery-2. ...
- ASP.NET Core 基础教程-约定 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 基础教程-约定 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 基础教程-约定 因为 ASP.NET Core 是可以跨平台的 ...
- Folly: Facebook Open-source Library Readme.md 和 Overview.md(感觉包含的东西并不多,还是Boost更有用)
folly/ For a high level overview see the README Components Below is a list of (some) Folly component ...
- WPF元素绑定
原文:WPF元素绑定 数据绑定简介:数据绑定是一种关系,该关系告诉WPF从源对象提取一些信息,并用这些信息设置目标对象的属性.目标属性是依赖项属性.源对象可以是任何内容,从另一个WPF元素乃至ADO. ...
- ng-alain 复用标签相关设置
排除复用 import { ReuseTabMatchMode, ReuseTabService } from '@delon/abc'; export class StartupService { ...
- AngularJS 计时器
<div ng-controller="MyController"> <!--显示$scope.clock的now属性--> <h1>hello ...