前言

框架:springboot+shiro+redis+vue

最近写前后端分离授权的对账平台系统,采取了shiro框架,若采用shiro默认的cookie进行授权验证时,一直存在由于跨域造成前端请求到的cookie每次都不相同,从而无法完成授权及验证的操作,即每次登陆成功时还是会显示未登陆。

Pom的引入

<dependency>

<groupId>org.crazycake</groupId>

<artifactId>shiro-redis</artifactId>

<version>3.0.0</version>

<exclusions>

<exclusion>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

</exclusion>

</exclusions>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring-boot-web-starter</artifactId>

<version>1.4.1</version>

</dependency>

代码的编写

public class CustomRealm extends AuthorizingRealm {

    @Resource
private AdminDao adminDao; @Resource
private PermissionDao permissionDao; @Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取登录认证成功的主体
Admin admin = (Admin)principals.getPrimaryPrincipal();
// HashMap<String,String> admin = (HashMap)principals.getPrimaryPrincipal();
//根据角色Id查询权限
List<String> perms = new ArrayList<>();
// if (!admin.get("roleId").equals("-1")){
// perms = permissionDao.findPermsByRoles(admin.get("roleId"));
// }
if (!admin.getRoleId().equals("-1")){
perms = permissionDao.findPermsByRoles(admin.getRoleId());
}
//对集合中的字符串做过滤处理,去除 空字符串及null
perms = perms.stream().filter(s->s != null && !s.isEmpty()).collect(Collectors.toList());
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(perms);
return info;
} @Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取通过subject提交的主体信息
String userName = (String)token.getPrincipal();
Admin admin = adminDao.queryByUserName(userName);
if(admin == null||admin.getFlag()==0){
throw new UnknownAccountException("账号不存在或账号不可用,请联系管理员");
}
// HashMap<String,String> map=new HashMap();
// map.put("username",admin.getUsername());
// map.put("roleId",admin.getRoleId());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(admin,admin.getPassword(),ByteSource.Util.bytes(admin.getSalt()),super.getName());
return info;
}
}

shiroConfig

@Configuration
public class ShiroConfig {
@Value("${redis.port}")
private Integer port; @Value("${redis.host}")
private String host; /**
* @author zhuyang
* @description 创建域,包括认证管理器,安全管理器
* @date 2021-03-07 15:55
* @param credentialsMatcher: MD5加密器
* @return com.yxkj.web.accountchecking.realm.CustomRealm
*/
@Bean
public CustomRealm customRealm(HashedCredentialsMatcher credentialsMatcher){
CustomRealm customRealm = new CustomRealm();
customRealm.setCredentialsMatcher(credentialsMatcher);
return customRealm;
}
/**
* @author zhuyang
* @description 安全管理器 shiro里面所有的权限控制,认证都事先通过他,安全管理器是和过滤器工厂打交道的桥梁
* @date 2021-03-07 15:54
* @param customRealm:
* @return org.apache.shiro.web.mgt.DefaultWebSecurityManager
*/
//只把记住我和域注入到安全管理器
//如果记住我注入到域中,那么安全管理器还要和域打交道,这样就绕了
@Bean
public DefaultWebSecurityManager securityManager(CustomRealm customRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm);
//添加开始
CarWashModularRealmAuthorizer authorizer = new CarWashModularRealmAuthorizer();
List<Realm> list=new ArrayList<>();
list.add(customRealm);
authorizer.setRealms(list);
securityManager.setAuthorizer(authorizer);
//将自定义的会话管理器注入到安全管理器中
securityManager.setSessionManager(sessionManger());
//将自定义的redis缓存管理器注册到安全管理器中
securityManager.setCacheManager(cacheManager());
//添加结束
return securityManager;
}
/**
* @author zhuyang
* @date 2021-03-07 15:57
* @param securityManager: 先经过过滤器工厂,再通过subject提交token给DefaultWebSecurityManager(安全管理器)
* @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
Map<String, Filter> filtersMap = new LinkedHashMap<String, Filter>();
//修改logout后的地址
filtersMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
LogoutFilter logout = new LogoutFilter();
logout.setRedirectUrl("/unauth");
filtersMap.put("logout",logout);
filterFactoryBean.setSecurityManager(securityManager);
filterFactoryBean.setUnauthorizedUrl("/UnauthorizedUrl");
Map map = new LinkedHashMap();
map.put("/admin/getVerifyCode","anon");
map.put("/admin/subLogin","anon");//访问登录
map.put("/swagger-ui.html","anon");
map.put("/admin/logout","logout");
map.put("/doc.html","anon");
map.put("/webjars/**","anon");
map.put("/swagger-resources","anon");
map.put("/freeQuery/manualCheck","anon");
map.put("/v2/**","anon");
// map.put("/**","anon");
map.put("/**","authc"); filterFactoryBean.setFilters(filtersMap);
filterFactoryBean.setFilterChainDefinitionMap(map);
filterFactoryBean.setLoginUrl("/unauth");
return filterFactoryBean;
} @Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("md5");
credentialsMatcher.setHashIterations(1024);
return credentialsMatcher;
} /**
* @author zhuyang
* @description 如果您在使用shiro注解配置的同时,引入了spring aop的starter,
* 会有一个奇怪的问题,导致shiro注解的请求,不能被映射,需加入以下配置
* @date 2021-03-07 16:14
* @return org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
/**
* setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
* 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,导致返回404。
* 加入这项配置能解决这个bug
*/
defaultAdvisorAutoProxyCreator.setUsePrefix(true);
return defaultAdvisorAutoProxyCreator;
} /**
* @author zhuyang
* @description 开启shiro注解的支持
* @date 2021-03-07 16:20
* @param securityManager:
* @return org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
} public CORSAuthenticationFilter corsAuthenticationFilter(){
return new CORSAuthenticationFilter();
} // @Bean
// public SimpleCookie simpleCookie(){
// SimpleCookie cookie = new SimpleCookie();
// cookie.setName("rememberMe");
// cookie.setMaxAge(18);
// return cookie;
// } /**
* @author zhuyang
* @description 1.redis的控制器,操作redis
* @date 2021-03-07 14:41
*/
public RedisManager redisManager(){
RedisManager redisManager=new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
redisManager.setTimeout(2000);
return redisManager;
} /**
* @author zhuyang
* @description sessiionDao
* @date 2021-03-06 17:31
*/
public RedisSessionDAO redisSessionDAO(){
RedisSessionDAO sessionDAO=new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
// sessionDAO.setExpire(2000);
return sessionDAO;
}
/**
* @author zhuyang
* @description 会话管理器
* @date 2021-03-06 17:30
*/
public DefaultWebSessionManager sessionManger(){
CustomSessionManager sessionManager=new CustomSessionManager();
sessionManager.setSessionDAO(redisSessionDAO());
sessionManager.setSessionIdCookieEnabled(false);
sessionManager.setSessionIdUrlRewritingEnabled(false);
// sessionManager.setTimeout(new DefaultSessionKey(),100);
return sessionManager;
} /**
* @author zhuyang
* @description 缓存管理器
* @date 2021-03-06 17:31
*/
public RedisCacheManager cacheManager(){
RedisCacheManager redisCacheManager=new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
}

SessionId的获取

public class CustomSessionManager extends DefaultWebSessionManager {

    @Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader("token");
//如果请求头中有 token 则其值为sessionId
if (!StringUtils.isEmpty(id)) {
//sessionId存放的位置
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
//sesssionId的值
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
//是否验证
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
//否则按默认规则从cookie取sessionId
return super.getSessionId(request, response);
}
} }

解决403的问题

public class CORSAuthenticationFilter extends FormAuthenticationFilter {
public CORSAuthenticationFilter() {
super();
} @Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if(request instanceof HttpServletRequest){
if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")){
System.out.println("OPTIONS请求");
return true;
}
}
return super.isAccessAllowed(request, response, mappedValue);
} }

授予管理员的一切权限,且不可修改

public class CarWashModularRealmAuthorizer extends ModularRealmAuthorizer {
@Override
public boolean isPermitted(PrincipalCollection principals, String permission){
// HashMap user = (HashMap) principals.getPrimaryPrincipal();
Admin user = (Admin) principals.getPrimaryPrincipal();
// 如果是管理员拥有所有的访问权限
return user.getUsername().equals("gly") || super.isPermitted(principals, permission);
}
@Override
public boolean hasRole(PrincipalCollection principals, String roleIdentifier) {
Admin user = (Admin) principals.getPrimaryPrincipal();
// 如果是管理员拥有所有的角色权限
return user.getUsername().equals("gly") || super.hasRole(principals, roleIdentifier);
}
}

异常拦截

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler { @ExceptionHandler(AuthorizationException.class)
@ResponseBody
public Map handleAuthorizationException(AuthorizationException e){
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "1000002");
map.put("msg", "该用户没有对应的权限");
return map;
} @ExceptionHandler(Exception.class)
@ResponseBody
public Result exceptionHandler(Exception e){
e.printStackTrace();
log.error("异常日志打印"+e.getMessage());
System.out.println(e.getClass().getName());
return new Result(500,e.getMessage());
}
}

其他处理

@Controller
@ApiIgnore
public class ShiroController {
@RequestMapping(value = "/unauth")
@ResponseBody
public Object unauth() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "1000000");
map.put("msg", "未登录");
return map;
} @RequestMapping(value = "/UnauthorizedUrl")
@ResponseBody
public Object UnauthorizedUrl() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("code", "1000001");
map.put("msg", "未授权");
return map;
}
}

shiro和redis集成,前后端分离的更多相关文章

  1. spring boot 2.0.0 + shiro + redis实现前后端分离的项目

    简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大 ...

  2. vue.js+UEditor集成 [前后端分离项目]

    首先,谈下这篇文章中的前后端所涉及到的技术框架内容. 虽然是后端的管理项目,但整体项目,是采用前后端分离的方式完成,这样做的目的也是产品化的需求: 前端,vue+vuex+vue router+web ...

  3. vue+Ueditor集成 [前后端分离项目][图片、文件上传][富文本编辑]

    后端DEMO:https://github.com/coderliguoqing/UeditorSpringboot 前端DEMO:https://github.com/coderliguoqing/ ...

  4. docker+nginx+redis部署前后端分离项目!!!

    介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...

  5. springboot集成shiro 前后端分离

    前后端分离情况下 首先考虑是否跨域,如果没有跨域是可以使用shiro原生的session+cookie,无需特别处理. 如果涉及到跨域则需要考虑cookie问题(本质上也是重写shiro获取JESSI ...

  6. 在前后端分离的SpringBoot项目中集成Shiro权限框架

    参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制   以及跨域的问题也有涉及

  7. 在前后端分离项目中使用SpringBoot集成Shiro

    前言 这次在处理一个小项目时用到了前后端分离,服务端使用springboot2.x.权限验证使用了Shiro.前后端分离首先需要解决的是跨域问题,POST接口跨域时会预发送一个OPTIONS请求,浏览 ...

  8. springboot集成shiro 前后端分离 统一处理shiro异常

    在前后端分离的情况下,shiro一些权限异常处理会返回401之类的结果,这种结果不好统一管理.我们希望的结果是统一管理,所有情况都受我们控制 就算权限验证失败,我们也希望返回200,并且返回我们定义的 ...

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

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

随机推荐

  1. 第三十一个知识点:Game Hopping证明

    第三十一个知识点:Game Hopping证明 关于安全证明, 目前主流的方法有安全归约证明 (由 single game 实现) 和 Game Hopping (由 game sequence 实现 ...

  2. 自动化集成:Docker容器入门简介

    前言:该系列文章,围绕持续集成:Jenkins+Docker+K8S相关组件,实现自动化管理源码编译.打包.镜像构建.部署等操作:本篇文章主要描述Docker基础用法. 一.Docker简介 1.基础 ...

  3. Java面向对象笔记 • 【第6章 Java常用类】

    全部章节   >>>> 本章目录 6.1 Object类 6.1.1 Object类概述 6.1.2 Object的常用方法 6.1.3 实践练习 6.2 String类和St ...

  4. EntityFrameworkCore数据迁移(一)

    .net core出来已经有很长一段时间了,而EentityFrameworkCore(后面简称EFCore)是.net framework的EntityFramework在.net core中的实现 ...

  5. Linux-saltstack-4 jinjia模板得基本使用

    @ 目录 一.简介 二.jinja2语法 1.jinja2变量 1.1 配置文件中使用jinja变量 1.2在脚本中定义jinja变量 1.3在脚本中设置grains变量 例子1:单值 例子2:多值 ...

  6. mysql编译报错

    1.make报错现象 Warning: Bison executable not found in PATH 解决办法 yum -y install bison 2.make报错现象 ake Erro ...

  7. vim 安装使用 pathogen

    目录 pathogen 是什么? 如何安装? pathogen 是什么? pathogen 一般作为 vim 新手的第一个插件,用来统一管理 vim 插件包. (官方解释)非常容易地管理你的 'run ...

  8. Linux上天之路(九)之文件和文件夹的权限

    主要内容 linux 基本权限 linux特殊权限 linux隐藏权限 linux file ACL 权限 1. Linux的基本权限 使用ls -l filename 命令查看文件或文件夹详细权限 ...

  9. node之module与fs文件系统

    命令行窗口(小黑屏).cmd窗口.终端.shell 开始菜单 --> 运行 --> CMD --> 回车 常用的指令: dir 列出当前目录下的所有文件 cd 目录名 进入到指定的目 ...

  10. dart系列之:手写Library,Library编写最佳实践

    目录 简介 使用part和part of src中的文件 package中的lib文件 总结 简介 Library是dart用来组织代码的一种非常有用的方式,通过定义不同的Library,可以将非常有 ...