SpringBoot学习(四)--SpringBoo集成shiro实现基础登陆认证和权限管理
2017-12-20 19:12
393 查看
版权声明:作者原创,转载请注明出处。
本系列文章目录地址:http://blog.csdn.net/u011961421/article/details/79416510
相比较Spring家族的Spring Security,Shiro在保持强大功能的同时,还具有简单灵活轻量等特点,学习成本也要低很多,目前受到越来越多项目系统的应用,更多关于shiro的介绍可以自己百度,这里不偏题。
一.引入依赖文件
注意有需要的可以同时引入shiro-ehcache实现缓存,我这边因为后面会集成使用redis,所以就不引入了。引入shiro-ehcache也并不复杂,多加一个配置可自行百度,对于分布式部署来说,没有必要使用shiro-ehcache。
二.基础表
这里介绍使用简单的用户+权限的表结构,需要三张表,用户表,角色表,以及关系表,建表sql如下
测试数据如下
三.配置shiro
SpringBoot中集成shiro时,因为省去了配置文件,所以需要编写配置类,使用@Configuration注解注入配置类。这里最简洁shiro的配置需要包含三个方法:
1.注册Realm至Spring Bean,Realm是认证和授权的具体实现,shiro对于使用者来说需要实现的一个方法,它的简洁之处也在于只需要实现这个接口便可使用shiro。
2.注册SecurityManager安全管理器,无特殊需求使用默认管理器便可,SecurityManager是shiro的主入口,该配置方法将用户自定义的Realm注入SecurityManager即可。
3.配置Filter访问策略,这里请看代码,相信详细注解一看就明白。如下:
注意shiro支持配置路径,页面标签,注解等等多种权限配置方式,和不同粒度的权限配置。
1.配置方式:
2.注解方式
RequiresAuthentication:使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证。
RequiresGuest:使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。
RequiresPermissions:当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresRoles:当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。
RequiresUser:当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
3.页面标签:博主使用较少,应用场景也比较少,可以自己百度,这里不做介绍。
四.编写MyRealm
正如第三点所说,因为shiro并不知道你登陆认证的具体逻辑和授权的具体逻辑,所以需要用户自己实现,继承AuthorizingRealm,实现doGetAuthorizationInfo(授权)和doGetAuthenticationInfo(登陆认证)两个抽象方法,具体代码如下:
五.登陆和授权验证
下面是登陆的controller:
页面:
编写完成后启动SpringBoot工程,访问不允许匿名访问的路径,会跳转到登陆页面,登陆成功后可查看自己所拥有的权限,同样可以访问相关路径测试是否授权成功。具体代码可以访问github(https://github.com/15651037763/cms)下载。
写在最后,这里介绍的只是最简单方式的使用,以上github地址为本系列文章项目地址,本人能力有限博客略有延迟,更多shiro功能可以关注github或持续更新,在学习和研究的同时,博客会做到尽量总结全面不遗漏。
本系列文章目录地址:http://blog.csdn.net/u011961421/article/details/79416510
简介
Shiro是Apache的一个开源安全框架,旨在简化身份验证和授权,主要用来处理身份认证,授权,企业会话管理和加密等,并且Shiro不依赖任何容器在JavaSE和JavaEE项目中都可以使用。相比较Spring家族的Spring Security,Shiro在保持强大功能的同时,还具有简单灵活轻量等特点,学习成本也要低很多,目前受到越来越多项目系统的应用,更多关于shiro的介绍可以自己百度,这里不偏题。
实战
以下为SpringBoot集成Shiro,实现基础登陆认证和权限管理的过程。一.引入依赖文件
<!-- shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency>
注意有需要的可以同时引入shiro-ehcache实现缓存,我这边因为后面会集成使用redis,所以就不引入了。引入shiro-ehcache也并不复杂,多加一个配置可自行百度,对于分布式部署来说,没有必要使用shiro-ehcache。
二.基础表
这里介绍使用简单的用户+权限的表结构,需要三张表,用户表,角色表,以及关系表,建表sql如下
create table CMS_USER_INFO ( ID integer(10) not null auto_increment comment '用户ID', USER_CODE varchar(20) not null comment '用户编码', USER_NAME varchar(64) not null comment '用户名称', USER_PWD varchar(150) comment '用户密码', REMARK varchar(60) comment '备注', CREATE_BY varchar(20) comment '创建人', CREATE_DATE datetime comment '创建时间', MODIFIED_BY varchar(20) comment '修改人', MODIFIED_DATE datetime comment '修改时间', SORTNO integer(2) default 0 comment '排序', STATE integer(2) comment '数据状态', primary key (ID) ); create table CMS_ROLE_INFO ( ID integer(10) not null auto_increment comment '角色编号', ROLE_CODE varchar(20) not null comment '角色编码', ROLE_NAME varchar(60) not null comment '角色名称', CREATE_BY varchar(20) comment '创建人', CREATE_DATE datetime comment '创建时间', MODIFIED_BY varchar(20) comment '修改人', MODIFIED_DATE datetime comment '修改时间', SORTNO integer(2) default 0 comment '排序', STATE integer(2) comment '数据状态', primary key (ID) ); create table CMS_USER_ROLE_R ( ID int not null auto_increment, USER_CODE varchar(10), ROLE_CODE varchar(10), CREATE_BY varchar(20) comment '创建人', CREATE_DATE datetime comment '创建时间', MODIFIED_BY varchar(20) comment '修改人', MODIFIED_DATE datetime comment '修改时间', SORTNO integer(2) default 0 comment '排序', STATE integer(2) comment '数据状态', primary key (ID) );
测试数据如下
INSERT INTO `cms_user_info` VALUES (1, 'admin', '管理员', 'd0970714757783e6cf17b26fb8e2298f', '测试备注', 'admin', '2017-12-8 20:32:02', 'admin', '2017-12-8 20:32:09', 0, 1); INSERT INTO `cms_user_info` VALUES (2, '17040406', '张三', 'd0970714757783e6cf17b26fb8e2298f', '测试备注', 'admin', '2017-12-8 20:32:02', 'admin', '2017-12-8 20:32:09', 1, 1); INSERT INTO `cms_role_info` VALUES (1, 'admin', '系统管理员', 'admin', '2017-12-8 19:06:35', 'admin', '2017-12-8 19:06:39', 0, 1); INSERT INTO `cms_role_info` VALUES (2, 'guest', '客人', 'admin', '2017-12-8 19:06:35', 'admin', '2017-12-8 19:06:35', 1, 1); INSERT INTO `cms_user_role_r` VALUES (1, 'admin', 'admin', 'admin', '2017-12-8 20:19:34', 'admin', '2017-12-8 20:19:34', 0, 1); INSERT INTO `cms_user_role_r` VALUES (2, '17040406', 'guest', 'admin', '2017-12-8 20:19:34', 'admin', '2017-12-8 20:19:34', 0, 1);
三.配置shiro
SpringBoot中集成shiro时,因为省去了配置文件,所以需要编写配置类,使用@Configuration注解注入配置类。这里最简洁shiro的配置需要包含三个方法:
1.注册Realm至Spring Bean,Realm是认证和授权的具体实现,shiro对于使用者来说需要实现的一个方法,它的简洁之处也在于只需要实现这个接口便可使用shiro。
2.注册SecurityManager安全管理器,无特殊需求使用默认管理器便可,SecurityManager是shiro的主入口,该配置方法将用户自定义的Realm注入SecurityManager即可。
3.配置Filter访问策略,这里请看代码,相信详细注解一看就明白。如下:
package com.pf.org.cms.common; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; /** * @Auther: pf * @Date: 2017/12/12 19:34 * @Description: shiro配置组件 */ @Configuration public class ShiroConfiguration { private static final Logger log = LoggerFactory.getLogger(ShiroConfiguration.class); /** * 注入Realm * @return MyRealm */ @Bean(name = "myRealm") public MyRealm myAuthRealm() { MyRealm myRealm = new MyRealm(); log.info("myRealm注册完成"); return myRealm; } /** * 注入SecurityManager * @param myRealm * @return SecurityManager */ @Bean(name = "securityManager") public SecurityManager securityManager(@Qualifier("myRealm")MyRealm myRealm) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(myRealm); log.info("securityManager注册完成"); return manager; } /** * 注入Filter * @param securityManager * @return ShiroFilterFactoryBean */ @Bean(name = "shiroFilter") public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); filterFactoryBean.setSecurityManager(securityManager); // 配置登录的url和登录成功的url filterFactoryBean.setLoginUrl("/auth/login"); filterFactoryBean.setSuccessUrl("/home"); // 配置未授权跳转页面 filterFactoryBean.setUnauthorizedUrl("/errorPage/403"); // 配置访问权限 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/css/**", "anon"); // 表示可以匿名访问 filterChainDefinitionMap.put("/fonts/**", "anon"); filterChainDefinitionMap.put("/imgs/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/auth/**", "anon"); filterChainDefinitionMap.put("/errorPage/**", "anon"); filterChainDefinitionMap.put("/demo/**", "anon"); filterChainDefinitionMap.put("/swagger-*/**", "anon"); filterChainDefinitionMap.put("/swagger-ui.html/**", "anon"); filterChainDefinitionMap.put("/webjars/**", "anon"); filterChainDefinitionMap.put("/v2/**", "anon"); filterChainDefinitionMap.put("/admin/**", "roles[admin]");// 表示admin权限才可以访问,多个加引号用逗号相隔 filterChainDefinitionMap.put("/*", "authc");// 表示需要认证才可以访问 filterChainDefinitionMap.put("/**", "authc"); filterChainDefinitionMap.put("/*.*", "authc"); filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); log.info("shiroFilter注册完成"); return filterFactoryBean; } }
注意shiro支持配置路径,页面标签,注解等等多种权限配置方式,和不同粒度的权限配置。
1.配置方式:
默认过滤器(10个) anon -- org.apache.shiro.web.filter.authc.AnonymousFilter authc -- org.apache.shiro.web.filter.authc.FormAuthenticationFilter authcBasic -- org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter perms -- org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter port -- org.apache.shiro.web.filter.authz.PortFilter rest -- org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter roles -- org.apache.shiro.web.filter.authz.RolesAuthorizationFilter ssl -- org.apache.shiro.web.filter.authz.SslFilter user -- org.apache.shiro.web.filter.authc.UserFilter logout -- org.apache.shiro.web.filter.authc.LogoutFilter anon:例子/admins/**=anon 没有参数,表示可以匿名使用。 authc:例如/admins/user/**=authc表示需要认证(登录)才能使用,没有参数 roles:例子/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,例如admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。 perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。 rest:例子/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等。 port:例子/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数。 authcBasic:例如/admins/user/**=authcBasic没有参数表示httpBasic认证 ssl:例子/admins/user/**=ssl没有参数,表示安全的url请求,协议为https user:例如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
2.注解方式
RequiresAuthentication:使用该注解标注的类,实例,方法在访问或调用时,当前Subject必须在当前session中已经过认证。
RequiresGuest:使用该注解标注的类,实例,方法在访问或调用时,当前Subject可以是“gust”身份,不需要经过认证或者在原先的session中存在记录。
RequiresPermissions:当前Subject需要拥有某些特定的权限时,才能执行被该注解标注的方法。如果当前Subject不具有这样的权限,则方法不会被执行。
RequiresRoles:当前Subject必须拥有所有指定的角色时,才能访问被该注解标注的方法。如果当天Subject不同时拥有所有指定角色,则方法不会执行还会抛出AuthorizationException异常。
RequiresUser:当前Subject必须是应用的用户,才能访问或调用被该注解标注的类,实例,方法。
3.页面标签:博主使用较少,应用场景也比较少,可以自己百度,这里不做介绍。
四.编写MyRealm
正如第三点所说,因为shiro并不知道你登陆认证的具体逻辑和授权的具体逻辑,所以需要用户自己实现,继承AuthorizingRealm,实现doGetAuthorizationInfo(授权)和doGetAuthenticationInfo(登陆认证)两个抽象方法,具体代码如下:
package com.pf.org.cms.common; import com.pf.org.cms.entity.UserInfo; import com.pf.org.cms.entity.UserRoleInfo; import com.pf.org.cms.service.UserService; import com.pf.org.cms.utils.MD5Util; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Auther: pf * @Date: 2017/12/12 19:29 * @Description: 认证和授权具体实现 */ public class MyRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 为当前subject授权 * @param principalCollection * @return AuthorizationInfo */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { Map<String, Object> params = new HashMap<>(); params.put("userCode", (String) super.getAvailablePrincipal(principalCollection)); List<UserRoleInfo> userRoleInfos = userService.getUserRoleInfos(params); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); if(!userRoleInfos.isEmpty()) { for(UserRoleInfo role : userRoleInfos) { info.addRole(role.getRoleCode()); } } return info; } /** * 认证登陆subject身份 * @param authenticationToken * @return AuthenticationInfo * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; Map<String, Object> params = new HashMap<>(); params.put("userCode", (String)authenticationToken.getPrincipal()); List<UserInfo> userInfos = userService.getUserInfos(params); if (userInfos.isEmpty()) { throw new UnknownAccountException(); } else if(userInfos.size() > 1) { throw new DisabledAccountException(); } else { UserInfo user = userInfos.get(0); // 校验密码 return new SimpleAuthenticationInfo(authenticationToken.getPrincipal(), user.getUserPwd(), ByteSource.Util.bytes("2w@W"), getName()); } } }
五.登陆和授权验证
下面是登陆的controller:
package com.pf.org.cms.web; import com.pf.org.cms.common.IConstants; import com.pf.org.cms.entity.JsonBean; import com.pf.org.cms.utils.ParamUtils; import org.apache.commons.collections.MapUtils; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.DisabledAccountException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import javax.servlet.http.HttpServletRequest; import java.util.Map; /** * @Auther: pf * @Date: 2017/12/12 19:41 * @Description: */ @Controller @RequestMapping(value = "/auth") public class AuthenticationController { private static final Logger log = LoggerFactory.getLogger(AuthenticationController.class); @RequestMapping(value = "/login") public String login() { return "/login"; } @ResponseBody @RequestMapping(value = "/login_in", produces = "application/json;charset=UTF-8") public JsonBean loginIn(HttpServletRequest request) { JsonBean reJson = new JsonBean(); Map paramMap = ParamUtils.handleServletParameter(request); String userCode = MapUtils.getString(paramMap, "userCode"); String userPwd = MapUtils.getString(paramMap, "userPwd"); // shiro认证 Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(userCode, userPwd); try { subject.login(token); } catch (UnknownAccountException e) { reJson.setMessage("账户不存在"); return reJson; } catch (DisabledAccountException e) { reJson.setMessage("账户存在问题"); return reJson; } catch (AuthenticationException e) { reJson.setMessage("密码错误"); return reJson; } catch (Exception e) { log.info("登陆异常", e); reJson.setMessage("登陆异常"); return reJson; } reJson.setStatus(IConstants.RESULT_INT_SUCCESS); String res = subject.getPrincipals().toString(); if (subject.hasRole("admin")) { res = res + "----------你拥有admin权限"; } if (subject.hasRole("guest")) { res = res + "----------你拥有guest权限"; } reJson.setData(res); reJson.setMessage("登陆成功"); return reJson; } }
页面:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <#include "/common/common.ftl"/> </head> <body> <div> <span>hello! please login!</span> </div> <div> <span>用户名:</span><input type="text" id="userCode" /> <span>密码:</span><input type="text" id="userPwd" /> </div> <div> <input type="button" onclick="submitLogin()" value="登陆"> </div> </body> </html> <script type="text/javascript" src="/js/md5.js"></script> <script type="text/javascript"> function submitLogin() { var userCode = $.trim($("#userCode").val()); var userPwd = $.trim($("#userPwd").val()); var hexPwd = hex_md5(userPwd); $.ajax({ type: 'POST', url: "http://localhost:8080/auth/login_in", data: {"userCode":userCode,"userPwd":hexPwd}, dataType:"json", success: function(rep){ if(rep.status == 0){ alert(rep.message); alert(rep.data); //window.location.reload(); // loadPage(0,baseUrl); }else{ alert(rep.message); } }, error:function(rep){ alert("获取信息失败!"); } }); } </script>
编写完成后启动SpringBoot工程,访问不允许匿名访问的路径,会跳转到登陆页面,登陆成功后可查看自己所拥有的权限,同样可以访问相关路径测试是否授权成功。具体代码可以访问github(https://github.com/15651037763/cms)下载。
写在最后,这里介绍的只是最简单方式的使用,以上github地址为本系列文章项目地址,本人能力有限博客略有延迟,更多shiro功能可以关注github或持续更新,在学习和研究的同时,博客会做到尽量总结全面不遗漏。
相关文章推荐
- shiro实现app web统一登陆认证和权限管理
- shiro 权限认证框集成到spring中,实现登陆与权限拦截
- 将 Shiro 作为应用的权限基础 三:基于注解实现的授权认证过程
- 权限管理之基于ACL的实现:自定义JSTL函数实现即时认证
- 以WCF安全认证方式调用通用权限管理系统获取基础信息资料
- 大公司业务流程审批组件【部门的员工—部门经理—部门副总—人力经理—人力副总】实现参考,强大的基础数据管理工具-C#.NET通用权限管理系统组件
- Oracle基础学习六之数据库权限及用户管理
- 将 Shiro 作为应用的权限基础 二:基于SpringMVC实现的认证过程
- 将 Shiro 作为应用的权限基础 三:基于注解实现的授权认证过程
- 基于AOP实现权限管理:通过shiro认证身份和模拟授权认证
- Linux基础学习3,Linux用户及文件权限管理,转载自实验楼
- (大数据工程师学习路径)第一步 Linux 基础入门----用户及文件权限管理
- 关于用jsp实现http认证安全登陆的学习笔记。(正在原创ing)
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(9)-TT模板的学习
- 黑马程序员 C#学习笔记⑥ 三层架构基础实现员工信息管理
- 用户权限设计 ASP.NET系统用户权限设计与实现、用户认证管理设计方案、通用数据权限管理系统设计
- ASP.NET MVC+EF框架+EasyUI实现权限管理系列(9)-TT模板的学习
- 通用权限管理系统组件 (GPM - General Permissions Manager) 中实现集团-分公司-分店-部门-员工的实体,连锁店业务系统的基础数据管理
- 权限管理 (一) 设计思路分析和实现授权、认证
- 通用权限管理系统组件 (GPM - General Permissions Manager) 中实现统一身份认证(Single Sign On,单点登录)附源码