shiro和redis集成,前后端分离
前言
框架: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集成,前后端分离的更多相关文章
- spring boot 2.0.0 + shiro + redis实现前后端分离的项目
简介 Apache Shiro是一个强大且易用的Java安全框架,执行身份验证.授权.密码学和会话管理.使用Shiro的易于理解的API,您可以快速.轻松地获得任何应用程序,从最小的移动应用程序到最大 ...
- vue.js+UEditor集成 [前后端分离项目]
首先,谈下这篇文章中的前后端所涉及到的技术框架内容. 虽然是后端的管理项目,但整体项目,是采用前后端分离的方式完成,这样做的目的也是产品化的需求: 前端,vue+vuex+vue router+web ...
- vue+Ueditor集成 [前后端分离项目][图片、文件上传][富文本编辑]
后端DEMO:https://github.com/coderliguoqing/UeditorSpringboot 前端DEMO:https://github.com/coderliguoqing/ ...
- docker+nginx+redis部署前后端分离项目!!!
介绍本文用的经典的前后端分离开源项目.项目的拉取这些在另一篇博客!!! 其中所需要的前后端打包本篇就不做操作了!!不明白的去看另一篇博客!!! 地址:http://www.cnblogs.com/ps ...
- springboot集成shiro 前后端分离
前后端分离情况下 首先考虑是否跨域,如果没有跨域是可以使用shiro原生的session+cookie,无需特别处理. 如果涉及到跨域则需要考虑cookie问题(本质上也是重写shiro获取JESSI ...
- 在前后端分离的SpringBoot项目中集成Shiro权限框架
参考[1].在前后端分离的SpringBoot项目中集成Shiro权限框架 参考[2]. Springboot + Vue + shiro 实现前后端分离.权限控制 以及跨域的问题也有涉及
- 在前后端分离项目中使用SpringBoot集成Shiro
前言 这次在处理一个小项目时用到了前后端分离,服务端使用springboot2.x.权限验证使用了Shiro.前后端分离首先需要解决的是跨域问题,POST接口跨域时会预发送一个OPTIONS请求,浏览 ...
- springboot集成shiro 前后端分离 统一处理shiro异常
在前后端分离的情况下,shiro一些权限异常处理会返回401之类的结果,这种结果不好统一管理.我们希望的结果是统一管理,所有情况都受我们控制 就算权限验证失败,我们也希望返回200,并且返回我们定义的 ...
- 前后端分离项目中后台集成shiro需要注意的二三事
1. 修改 Shiro 认证失败后默认重定向处理问题 a. 继承需要使用的 ShiroFilter,重载 onAccessDenied() 方法: @Override protected boolea ...
随机推荐
- Oracle 11g安装和PL/SQL连接完全解读(连接本地数据库)
Oracle安装是很多新手都烦恼的事情,各种奇怪的错误,并且还无从下手解决.我也隔了两年没有用Oracle了,昨天安装了一下,出现很多问题,也很苦恼.现在将安装过程详细记录下来,一来方便自己下次安装, ...
- 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 ...
- CS5211|DP转LVDS |低成本DP to LVDS Conversion 方案设计
目前市面上DP转LVDS转换--DP to LVDS Conversion 方案设计有以下: 龙迅LT8911 LT7211.普瑞PS8625.昆泰CH7511等方案,DP换LVDS转换主要是用在一些 ...
- SpringBoot集成Actuator健康指示器health
1.说明 本文详细介绍Actuator提供的HealthIndicators, 即健康指示器的配置使用, 利用自动配置的健康指标, 检查正在运行的应用程序的状态, 以及自定义健康指标的方法. 监控软件 ...
- Oracle数据库安装Version12c
1.安装规划 Oracle数据库版本: Linuxamd64_12102_database 12c Linux服务器系统: CentOS Linux release 7.5.1804 (Core) 6 ...
- 解决eclipse中Findbugs检查不生效的问题
eclipse安装了Findbugs插件, 但是在eclipse中发现不了bug错误, 具体表现为指定的类存在findbugs, 已经通过其他工具检查出来, 但是在eclipse中就是无法报告错误. ...
- TypeScript 中文教程之缩小----部分翻译自TS官方
Narrowing概念:字面意思是缩小,可以理解为细化或者您觉得更好的代名词. TS官方在这里做了很详细的说明,文字较多,简单以图片概括: typeof type guards 类型防护过程,可以通 ...
- Golang 简洁架构实战
文中项目代码位置:https://github.com/devYun/go-clean-architecture 转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.l ...
- Mysql字符串字段判断是否包含某个字符串的方法
方法一:like SELECT * FROM 表名 WHERE 字段名 like "%字符%"; 方法二:find_in_set() 利用mysql 字符串函数 find_in_s ...
- Apache Ant: If 和 Unless
目录 If And Unless If And Unless 从 Ant 1.9.1 起,可以在所有的任务和嵌套的元素上以特别的命名空间添加 if 和 unless 属性. In order to u ...