前言

框架: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. Oracle 11g安装和PL/SQL连接完全解读(连接本地数据库)

    Oracle安装是很多新手都烦恼的事情,各种奇怪的错误,并且还无从下手解决.我也隔了两年没有用Oracle了,昨天安装了一下,出现很多问题,也很苦恼.现在将安装过程详细记录下来,一来方便自己下次安装, ...

  2. CS5266 Type-C转HDMI+PD3.0+USB3.0 三合一拓展坞电路设计

    CS5266 Type-C转HDMI+PD3.0+USB3.0 三合一拓展坞电路设计 CS5266是一款带PD3.0快充 Type-C转HDMI 4K30HZ音视频转换芯片.CS5266支持PD3.0 ...

  3. CS5211|DP转LVDS |低成本DP to LVDS Conversion 方案设计

    目前市面上DP转LVDS转换--DP to LVDS Conversion 方案设计有以下: 龙迅LT8911 LT7211.普瑞PS8625.昆泰CH7511等方案,DP换LVDS转换主要是用在一些 ...

  4. SpringBoot集成Actuator健康指示器health

    1.说明 本文详细介绍Actuator提供的HealthIndicators, 即健康指示器的配置使用, 利用自动配置的健康指标, 检查正在运行的应用程序的状态, 以及自定义健康指标的方法. 监控软件 ...

  5. Oracle数据库安装Version12c

    1.安装规划 Oracle数据库版本: Linuxamd64_12102_database 12c Linux服务器系统: CentOS Linux release 7.5.1804 (Core) 6 ...

  6. 解决eclipse中Findbugs检查不生效的问题

    eclipse安装了Findbugs插件, 但是在eclipse中发现不了bug错误, 具体表现为指定的类存在findbugs, 已经通过其他工具检查出来, 但是在eclipse中就是无法报告错误. ...

  7. TypeScript 中文教程之缩小----部分翻译自TS官方

    Narrowing概念:字面意思是缩小,可以理解为细化或者您觉得更好的代名词. TS官方在这里做了很详细的说明,文字较多,简单以图片概括: typeof  type guards 类型防护过程,可以通 ...

  8. Golang 简洁架构实战

    文中项目代码位置:https://github.com/devYun/go-clean-architecture 转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.l ...

  9. Mysql字符串字段判断是否包含某个字符串的方法

    方法一:like SELECT * FROM 表名 WHERE 字段名 like "%字符%"; 方法二:find_in_set() 利用mysql 字符串函数 find_in_s ...

  10. Apache Ant: If 和 Unless

    目录 If And Unless If And Unless 从 Ant 1.9.1 起,可以在所有的任务和嵌套的元素上以特别的命名空间添加 if 和 unless 属性. In order to u ...