前言

框架: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. 解决ubuntu突然无法联网问题

    一.问题描述 今天使用笔记本远程办公的时候,突然电脑无法联网了,使用chrome浏览器访问网页出现如下错误 This site can't be reachedwww.baidu.com's serv ...

  2. Kernel PCA and De-Noisingin Feature Spaces

    目录 引 主要内容 Kernel PCA and De-Noisingin Feature Spaces 引 kernel PCA通过\(k(x,y)\)隐式地将样本由输入空间映射到高维空间\(F\) ...

  3. 代码质量管理sonarqube部署使用

    一.sonarqube的部署 1.下载sonaqube:https://www.sonarqube.org/downloads/ 根据需要下载特定版本: 2.如果通过sonar-scanner进行代码 ...

  4. CapstoneCS5212|DP to VGA|CS5212设计电路方案

    CS5212功能概述 CS5212是一款DisplayPort端口到VGA转换器,它结合了DisplayPort输入接口和模拟RGB DAC输出接口.嵌入式单片机基于工业标准8051核心. CS521 ...

  5. MySQL高级查询与编程笔记 • 【目录】

    章节 内容 实践练习 MySQL高级查询与编程作业目录(作业笔记) 第1章 MySQL高级查询与编程笔记 • [第1章 数据库设计原理与实战] 第2章 MySQL高级查询与编程笔记 • [第2章 数据 ...

  6. django中的时区问题

    在django中设置时区,通过setting文件中的: TIME_ZONE = 'Asia/Shanghai' 开起多时区支持功能:USE_TZ=True 这时在数据库中插入的时间为UTC时间,当调用 ...

  7. Linux驱动实践:中断处理函数如何【发送信号】给应用层?

    作 者:道哥,10+年嵌入式开发老兵,专注于:C/C++.嵌入式.Linux. 关注下方公众号,回复[书籍],获取 Linux.嵌入式领域经典书籍:回复[PDF],获取所有原创文章( PDF 格式). ...

  8. Vulnhub系列:Tomato(文件包含getshell)

    这个靶机挺有意思,它是通过文件包含漏洞进行的getshell,主要姿势是将含有一句话木马的内容记录到ssh的登录日志中,然后利用文件包含漏洞进行包含,从而拿到shell 0x01 靶机信息 靶机:To ...

  9. 《Java核心技术·卷Ⅰ:基础知识(原版10》学习笔记 第5章 继承

    <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 目录 <Java核心技术·卷Ⅰ:基础知识(原版10>学习笔记 第5章 继承 5.1 类.超类和子类 5.1 ...

  10. 动态多条件mysql模糊查询

    sql拼接函数 public static String Instructor_sql_whole_study(String[] val_ids,String[] val_values) { Stri ...