之前我在网上找了很久的使用基于springcloud用shiro来作为权限控制。但是都没有一个好的demo,所以自己查了资料然后问了一些人,写了一个简单的demo。好了,话不多少直接进入正题

由于是使用springcloud,那么关于springcloud的一些知识我在这就不一一去讲了。直接讲一些shiro关键的地方。不懂的朋友可以去看看这位大牛写的帖子https://www.fangzhipeng.com/archive/?tag=SpringCloud 真的写的很好,springcloud快速入门

这里说一下我的环境配置:JDK11,TOMCAT9

首先我们需要创建一个server项目,然后创建一个zuul项目(由于现在很多服务架构都会用到负载均衡,所以我没有把权限和zuul放在一起,而是由zuul转发)。

OK,创建好这两个项目之后,我们开始搭建权限项目。

在pom中引入这两个对应的jar包

<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-ehcache</artifactId><version>1.4.0</version>
</dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version>
</dependency>

然后去创建关于shiro的配置文件

这两个类在网上都可以搜到,千篇一律的配置,只是根据个人不同的需求去更改其中过滤的位置。

首先创建一个配置文件,开头使用@Configuration去注解,让其成为一个配置文件。这边说明一下,由于是使用分布式,而且我这边没有使用包含的页面,所以没有去设置任何界面有关的权限配置。

@Configuration
public class ShiroConfig {@Beanpublic ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {System.out.println("ShiroConfiguration.shirFilter()");ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);//拦截器.Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();// 配置不会被拦截的链接 顺序判断//filterChainDefinitionMap.put("/static/**", "anon");//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了filterChainDefinitionMap.put("/logout", "logout");//<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;//<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->//filterChainDefinitionMap.put("/userInfo/**", "authc");// 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面//shiroFilterFactoryBean.setLoginUrl("/login");// 登录成功后要跳转的链接//shiroFilterFactoryBean.setSuccessUrl("/index");//未授权界面;shiroFilterFactoryBean.setUnauthorizedUrl("/response/responseMsg");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilterFactoryBean;}@Beanpublic MyShiroRealm myShiroRealm(){MyShiroRealm myShiroRealm = new MyShiroRealm();return myShiroRealm;}@Beanpublic SecurityManager securityManager(){DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();securityManager.setRealm(myShiroRealm());return securityManager;}/***  开启shiro aop注解支持.*  使用代理方式;所以需要开启代码支持;* @param securityManager* @return*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}@Bean(name="simpleMappingExceptionResolver")public SimpleMappingExceptionResolvercreateSimpleMappingExceptionResolver() {SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();Properties mappings = new Properties();mappings.setProperty("DatabaseException", "databaseError");//数据库异常处理mappings.setProperty("UnauthorizedException","/response/responseMsg");//无权限跳入的方法r.setExceptionMappings(mappings);  // None by defaultr.setDefaultErrorView("error");    // No defaultr.setExceptionAttribute("ex");     // Default is "exception"//r.setWarnLogCategory("example.MvcLogger");     // No defaultreturn r;}
}

然后创建一个针对于登陆和权限验证的类,说明一下,我这边使用的fegin,数据库也没有在这个权限项目,是去调用的其他服务的接口。当然了,密码验证这个段代码,可以根据自己的需求去改写。使用MD5也好,不要盐也好。我这边没有写的那么复杂

public class MyShiroRealm extends AuthorizingRealm {@Autowiredprivate UserService userService;/*** 角色* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("权限配置-->MyShiroRealm.doGetAuthorizationInfo()");SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();UserInfo userInfo  = (UserInfo)principals.getPrimaryPrincipal();for(SysRole role:userInfo.getRoleList()){authorizationInfo.addRole(role.getRole());for(SysPermission p:role.getPermissions()){authorizationInfo.addStringPermission(p.getPermission());}}return authorizationInfo;}/*主要是用来进行身份认证的,也就是说验证用户输入的账号和密码是否正确。*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {System.out.println("MyShiroRealm.doGetAuthenticationInfo()");//获取用户的输入的账号.String username = (String)token.getPrincipal();System.out.println(token.getCredentials());//通过username从数据库中查找 User对象,如果找到,没找到.//实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法UserInfo userInfo = userService.findByUsername(username);System.out.println("----->>userInfo="+userInfo);if(userInfo == null){return null;}if(!userInfo.getPassword().equals(new String((char[])token.getCredentials()))){throw new IncorrectCredentialsException("密码错误--------------"); //如果密码错误}SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, //用户名userInfo.getPassword(), //密码ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//salt=username+saltthis.getName()  //realm name);return authenticationInfo;}}

这是我的业务层的代码,是对应我另一个项目的方法

这个是我登陆的接口,由权限这个项目直接去验证。好处是因为由于加入权限这个要求的话,所有的接口都需要从权限这个项目进行发送。

@RequestMapping("/login")
public UserInfo login(HttpServletRequest request, Map<String, Object> map, String username, String password) throws Exception{Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username,password);String error = null;UserInfo userInfo = null;try {subject.login(token);userInfo = (UserInfo) SecurityUtils.getSubject().getPrincipal();//token.setRememberMe(true);} catch (UnknownAccountException e) {error = "用户名/密码错误";} catch (IncorrectCredentialsException e) {error = "用户名/密码错误";} catch (ExcessiveAttemptsException e) {// TODO: handle exceptionerror = "登录失败多次,账户锁定10分钟";} catch (AuthenticationException e) {// 其他错误,比如锁定,如果想单独处理请单独catch处理error = "其他错误:" + e.getMessage();}if (error != null) {// 出错了,返回登录页面//request.setAttribute("error", error);return null;}if(userInfo!=null){return userInfo;}return null;
}

执行到subject.login 这句话之后,会直接进入我们之前写的密码验证那个方法。

这个是无权限验证之后,会进入的方法

@RestController
@RequestMapping(value = "/response")
public class ResponseController {@RequestMapping(value = "/responseMsg")public String responseMsg(){return "failure";}}

这个是user项目的实体类

@Entity
@Table(name = "userinfo")
public class UserInfo  implements Serializable {@Id@GeneratedValueprivate Integer uid;@Column(unique =true)private String username;//帐号private String name;//名称(昵称或者真实姓名,不同系统不同定义)private String password; //密码;private String salt;//加密密码的盐private byte state;//用户状态,0:创建未认证(比如没有激活,没有输入验证码等等)--等待验证的用户 , 1:正常状态,2:用户被锁定.@ManyToMany(fetch= FetchType.EAGER)//立即从数据库中进行加载数据;@JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "uid") }, inverseJoinColumns ={@JoinColumn(name = "roleId") })private List<SysRole> roleList;// 一个用户具有多个角色public UserInfo() {}public Integer getUid() {return uid;}public void setUid(Integer uid) {this.uid = uid;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getSalt() {return salt;}public void setSalt(String salt) {this.salt = salt;}public byte getState() {return state;}public void setState(byte state) {this.state = state;}public List<SysRole> getRoleList() {return roleList;}public void setRoleList(List<SysRole> roleList) {this.roleList = roleList;}/*** 密码盐.* @return*/public String getCredentialsSalt(){return this.username+this.salt;}//重新对盐重新进行了定义,用户名+salt,这样就更加不容易被破解@Overridepublic String toString() {return "UserInfo{" +"uid=" + uid +", username='" + username + '\'' +", name='" + name + '\'' +", password='" + password + '\'' +", salt='" + salt + '\'' +", state=" + state +'}';}
}
@Entity
@Table(name = "sysrole")
public class SysRole {@Id@GeneratedValueprivate Integer id; // 编号private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的:private String description; // 角色描述,UI界面显示使用private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户//角色 -- 权限关系:多对多关系;@ManyToMany(fetch= FetchType.EAGER)@JoinTable(name="SysRolePermission",joinColumns={@JoinColumn(name="roleId")},inverseJoinColumns={@JoinColumn(name="permissionId")})private List<SysPermission> permissions;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getRole() {return role;}public void setRole(String role) {this.role = role;}public String getDescription() {return description;}public void setDescription(String description) {this.description = description;}public Boolean getAvailable() {return available;}public void setAvailable(Boolean available) {this.available = available;}public List<SysPermission> getPermissions() {return permissions;}public void setPermissions(List<SysPermission> permissions) {this.permissions = permissions;}}
@Entity
@Table(name = "syspermission")
public class SysPermission implements Serializable {@Id@GeneratedValueprivate Integer id;//主键.private String name;//名称.@Column(columnDefinition="enum('menu','button')")private String resourceType;//资源类型,[menu|button]private String url;//资源路径.private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:viewprivate Long parentId; //父编号private String parentIds; //父编号列表private Boolean available = Boolean.FALSE;public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getResourceType() {return resourceType;}public void setResourceType(String resourceType) {this.resourceType = resourceType;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getPermission() {return permission;}public void setPermission(String permission) {this.permission = permission;}public Long getParentId() {return parentId;}public void setParentId(Long parentId) {this.parentId = parentId;}public String getParentIds() {return parentIds;}public void setParentIds(String parentIds) {this.parentIds = parentIds;}public Boolean getAvailable() {return available;}public void setAvailable(Boolean available) {this.available = available;}}

这里都是多对多的关系,这里建议使用单向多对多。不然从数据取出值的时候会报出内存溢出的异常,网上说是由于双向关联的关系造成的,所以我把改成了单向的了。由于我在权限项目调用了user项目的方法,所以这里需要去写一个通过用户名查询的语句。这个简单的hql语句我就不贴出来了。

这里有个重点的地方是权限项目必须也拥有user项目的实体bean类。只需要单纯的类即可。

ok 我们试一下 这个我的几个项目

zuul转发,security权限控制,user用户

登陆

说一下我报这个错的问题,是由于userinfo这个多对多的原因。zuul转发之后,userinfo成功发送回来。但是无法转换格式,所以报错了。我之前是想通过通过和token的键值对形式传入redis中(token和userinfo)。但是多对多关系的类好像不行。我准备换成一对多形式。如果哪位朋友有解决方案也可以告诉我一下,在这谢谢了。

你们可以忽略这个问题,直接zuul转发即可,不需要去做其他操作。

这个是我的权限控制。我这个账户只有  userInfo:add 权限 ,没有  userInfo:del 权限

看下结果

ok成功了,这里提一点。由于分布式的原因,zuul每次转发的seesion都不一致,所以会和shiro的机制冲突。导致shiro无法去执行操作。需要在zuul配置文件中加一句

zuul.routes.oaUser.sensitiveHeaders="*"  //会过滤客户端请求中的和该配置项匹配的headers

demo地址如下:

springcloud-shiro_zuul整合shiro-Java代码类资源-CSDN下载

关于springcloud使用shiro的权限控制相关推荐

  1. shiro进行权限控制的四种方式

    我们使用shiro进行权限控制 有以下几种方式 1. URL拦截权限控制:基于filter过滤器实现 我们在spring配置文件中配置shiroFilter时配置 /css/ = anon /js/ ...

  2. SpringBoot集成Shiro进行权限控制和管理

    1 . 添加依赖 <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-sp ...

  3. 代理对象之spring注解事务控制或shiro注解权限控制的理解

    通过注解 代理的可以是service或者action等,当他们被调用时由代理对象生效,由框架控制 shiro Action方法注解权限控制 底层基于代理技术实现,为Action创建代理对象,由代理对象 ...

  4. springboot整合shiro实现权限控制

    博主简介:原互联网大厂tencent员工,网安巨头Venustech员工,阿里云开发社区专家博主,微信公众号java基础笔记优质创作者,csdn优质创作博主,创业者,知识共享者,欢迎关注,点赞,收藏. ...

  5. 【Shiro】权限控制注解

    Shiro共有5个注解: RequiresAuthentication: 使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证. RequiresGue ...

  6. SpringBoot整合Shiro实现权限控制,验证码

    本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...

  7. Apache Shiro的运行流程和权限控制方式分析

    Apache Shiro的运行流程和权限控制方式分析 Shiro的架构有3个主要的概念: Subject SecurityManager Realms 下面的关系图是关于这些组件是如何交互的高级概述. ...

  8. shiro权限控制登陆成功页面跳转问题

    在开发中使用了shiro进行权限控制,遇到一个页面跳转问题:当用户账号密码都正确的时候并没有跳转到登陆成功页面. 在shiroFilter过滤器中配置了登陆成功路径没有反应.注意:我使用的是表单验证. ...

  9. zeppelin使用shiro进行权限验证

    zeppelin使用shiro进行权限控制,默认情况下是anonymous,即不用登录即可使用.如果要增加登录验证,可以按照下列步骤操作.修改配置后需要重启才能生效. cd ${zeppelin} . ...

最新文章

  1. 【转】linux中waitpid及wait的用法
  2. 【Java基础】对象拷贝
  3. 国2c语言中指针与数组的赋值运算,C语言到汇编-指针与数组2
  4. 集合类-HashSet去重
  5. Android高通平台调试Camera驱动全纪录
  6. sort--排序函数
  7. 大数据洞察画像自动化实践
  8. css画横线箭头_2020年你应该关注这50款前端热门工具:CSS HTML 工具篇
  9. 浅谈构建 apache 2 虚拟主机[zt]
  10. 计算器的改良(洛谷-P1022)
  11. shell 两时间之差
  12. 2021-2025年中国触摸传感器行业市场供需与战略研究报告
  13. sysbench压测cpu,io,memory,threads,mutex
  14. 进程中堆栈向下增长的原因
  15. emc re 整改 超标_资深EMC工程师总结:EMC整改流程及常见问题
  16. oracle恢复几天前的数据,恢复oracle数据到以前的某个时间点
  17. Spring Boot学习笔记
  18. ps 绘制的 路径丢失了
  19. 定时语音提醒软件实现
  20. 小游戏:红色警戒争霸战!

热门文章

  1. 四非人保研上岸日记--去向:北科学硕(各种大佬们就别来了,这是给双非萌新的攻略)
  2. (附源码)计算机毕业设计SSM欢迎智能停车场管理系统
  3. 【HTML粒子波浪特效】(效果+代码)
  4. 《高级语言程序设计(C)》课程设计题目[2023-02-13]
  5. shell实例手册 2021-4-10
  6. win10安装mysql5.7.32和centos7安装mysql5.7
  7. 蓝牙HCI剖析(一)
  8. JavaScript匀速运动
  9. Android 指定纯色图标的颜色
  10. linux内存管理之 ION 内存管理器浅析Ⅱ(system contig heap)