Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。 Shiro有三个核心的概念:Subject、SecurityManager和Realms。 Subject: Subject实质上是一个当前执行用户的特定的安全“视图”,开发者所写的应用代码就通过Subject与Shiro框架进行交互。所有Subject实例都必须绑定到一个SecurityManager上,当使用一个Subject实例时,Subject实例会和SecurityManager进行交互,完成相应操作。 SecurityManager: SecurityManager是Shiro的核心部分,作为一种“保护伞”对象来协调内部安全组件共同构成一个对象图。开发人员并不直接操作SecurityManager,而是通过Subject来操作SecurityManager来完成各种安全相关操作。 Realms: Realms担当Shiro和应用程序的安全数据之间的“桥梁”或“连接器”。从本质来讲,Realm是一个特定安全的DAO,Realm中封装了数据操作的模块和用户自定义的认证匹配过程。SecurityManager可能配置多个Realms,但至少要有一个。 在shiroFilter的配置中配置了要过滤的目录,其中一个号表示匹配当前目录后的参数,两个号表示匹配该目录及其子目录及参数。等号后面是Shiro各类过滤器的简称 1、maven中添加Shiro以及缓存的依赖 2、在web.xml添加核心过滤器 3、新建spring-shiro.xml配置文件配置Shiro框架(同时让Spring监听器监听此文件) spring-shiro.xml 4、Ehcache的配置文件 ehcache-shiro.xml 5、编写自定义reaml 6、自定义表单认证过滤器 7、控制器中方法(示例) 8、后台数据库设计(参考)
Shiro安全框架的使用(记录)
框架介绍
使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。Shrio的主要功能:
基本原理
Shiro的基本架构:
Shiro过滤器
只解释了几个常用的,日后补充
Filter Name
Class
explanation
anon
org.apache.shiro.web.filter.authc.AnonymousFilter
匿名过滤器
authc
org.apache.shiro.web.filter.authc.FormAuthenticationFilter
表单认证过滤器
authcBasic
org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
authcBearer
org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter
logout
org.apache.shiro.web.filter.authc.LogoutFilter
退出登录过滤器
noSessionCreation
org.apache.shiro.web.filter.session.NoSessionCreationFilter
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
记住我以后默认访问
缓存文件参数解释
FileName
explanation
eternal
缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期
maxElementsInMemory
缓存中允许创建的最大对象数
overflowToDisk
内存不足时,是否启用磁盘缓存
timeToIdleSeconds
缓存数据的钝化时间,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间
timeToLiveSeconds
缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间
memoryStoreEvictionPolicy
缓存满了之后的淘汰算法
diskPersistent:
定在虚拟机重启时是否进行磁盘存储,默认为false
diskExpiryThreadIntervalSeconds:
属性可以设置该线程执行的间隔时间(默认是120秒,不能太小
FIFO
先进先出
LFU
最少被使用,缓存的元素有一个hit属性,hit值最小的将会被清出缓存
LRU
最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存
使用方法
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <shiro.version>1.2.3</shiro.version> </properties> <!--shiro框架的依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>${shiro.version}</version> </dependency> <!--shiro和spring的集成包--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-quartz</artifactId> <version>${shiro.version}</version> </dependency> <!--缓存依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro.version}</version> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache-core</artifactId> <version>2.6.0</version> </dependency>
<!--配置过滤器,让Spring管理Shiro框架--> <filter> <filter-name>shiroFilter</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> <!--将Shiro框架过滤器对应的生命周期交还给tomcat--> <init-param> <param-name>targetFilterLifecycle</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>shiroFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
为显示高亮效果,所有/**均写成了/ **(含空格),写的时候记得改过来<!--配置spring监听器,默认只加载WEB-INF目录下的spring.xml配置文件--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!--设置配置文件路径--> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring.xml,classpath:spring-shiro.xml</param-value> </context-param>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="https://www.springframework.org/schema/beans" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置Shiro框架--> <!-- Shiro的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <!--注入安全管理器--> <property name="securityManager" ref="securityManager"></property> <!--认证成功页面,认证成功以后访问的页面--> <property name="successUrl" value="/index.jsp"></property> <!--认证失败页面,没有认证强制访问需要认证的页面自动跳转的页面--> <property name="loginUrl" value="/admin/login"></property> <!--自定义的filter--> <property name="filters"> <map> <!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中 --> <entry key="authc" value-ref="formAuthenticationFilter"/> </map> </property> <!--配置过滤器链--> <!-- 过虑器链定义,从上向下顺序执行,一般将/ ** 放在最下边 --> <property name="filterChainDefinitions"> <value> <!--静态资源放行(匿名过滤器)anon org.apache.shiro.web.filter.authc.AnonymousFilter--> /lib/ **=anon /static/ **=anon <!--页面--> /verifyCode.jsp = anon /login.jsp = anon /index.jsp = anon /welcome.jsp = anon <!--记住我以后访问的默认页面--> /index.jsp = user <!--退出登录过滤器 logout org.apache.shiro.web.filter.authc.LogoutFilter--> /logout = logout <!--表单认证过滤器 authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter--> <!-- -/ **=authc 表示所有的url都必须认证通过才可以访问- --> / ** = authc </value> </property> </bean> <!--安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--注入自定义realm--> <property name="realm" ref="customRealm"></property> <!--注入缓存管理器(防止用户授权操作一直反复执行)--> <property name="cacheManager"> <bean class="org.apache.shiro.cache.ehcache.EhCacheManager"> <!--缓存配置文件--> <property name="cacheManagerConfigFile" value="classpath:ehcache-shiro.xml"/> </bean> </property> <!--注入会话管理器--> <property name="sessionManager" ref="sessionManager"></property> <!--记住我cookie--> <property name="rememberMeManager" ref="rememberMeManager"></property> </bean> <!--配置会话管理器(记住我功能)--> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!--删除残留的session(退出以后删除session)--> <property name="deleteInvalidSessions" value="true"/> <!--设置session时长 单位毫秒 1000*3600表示一小时--> <property name="globalSessionTimeout" value="#{1000 * 3600}"/> </bean> <!--记住我--> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie"> <bean class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <!--设置cookie时长 单位是秒--> <property name="maxAge" value="#{3600 * 24 * 3}"/> </bean> </property> </bean> <!--自定义realm--> <bean id="customRealm" class="com.shadow.shiro.CustomRealm"> <!--注入凭证匹配器(密码加密)--> <property name="credentialsMatcher" ref="credentialsMatcher"></property> </bean> <!--数据库中存储到的md5的散列值,在realm中需要设置数据库中的散列值它使用散列算法及散列次数,让shiro进行散列对比时和原始数据库中的散列值使用的算法一致--> <!--配置凭证匹配器(密码加密)--> <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher"> <!--指定加密算法--> <property name="hashAlgorithmName" value="MD5"></property> <!--散列次数--> <property name="hashIterations" value="3"></property> </bean> <!--自定义的from认证过滤器--> <bean id="formAuthenticationFilter" class="com.shadow.shiro.MyFormAuthenticationFilter"> <!-- 表单中账号的input名称 --> <property name="usernameParam" value="username"/> <!-- 表单中密码的input名称 --> <property name="passwordParam" value="password"/> <property name="rememberMeParam" value="rememberMe"/> </bean> <!--完成授权操作*****************************--> <!--shiro为集成springMVC拦截异常--> <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"> <property name="exceptionMappings"> <props> <!--没有权限异常跳转的页面--> <prop key="org.apache.shiro.authz.UnauthorizedException">unauthorized</prop> </props> </property> </bean> <!--开启aop,对代理类--> <aop:config proxy-target-class="true"/> <!--开启shiro的注解支持--> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <!--注入安全管理器--> <property name="securityManager" ref="securityManager"></property> </bean> </beans>
<?xml version="1.0" encoding="UTF-8"?> <ehcache updateCheck="false" name="shiroCache"> <!--缓存数据持久化的地址--> <diskStore path="C:shiroehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="false" diskPersistent="false" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU" /> </ehcache>
public class CustomRealm extends AuthorizingRealm { /** *判断当前的请求中是否包含username和password表单参数 * 1、如果没有 * 说明当前不是登录页面,就判断当前Shiro框架是否认证过 * 如果认证过 放行 * 如果没有认证,跳转认证失败页面 <property name="loginUrl" value="/admin/login"></property> * 2、如果有 * 进行realm自定义认证 * 认证成功 进认证成功页面 <property name="successUrl" value="index.jsp"></property> * 认证失败 获取认证失败的错误信息,再shiro框架的FormAuthenticationFilter过滤器中共享admin/login 获取失败errorMsg,显示到login.jsp上 * 自定义认证方法 * 自定义认证思路: * 1、注入IUserService,service层 * @Autowired * public IUserService userService; * 2、从token获取身份(账号信息) * String username = (String)token.getPrincipal(); * 3、调用IUserService层根据账号查询出用户信息的方法 * User user = userService.findUserByName(username); * 3.1 users不为null,说明账号存在 * 3.2 users为null,当前方法直接返回null * 4、创建认证信息对象,将身份(数据查询的那个user对象,和user对象的密码(凭证)作为参数)传递出去 * @param token * @return * @throws AuthenticationException */ @Autowired private IUserService userService; @Autowired private IPermissionService permissionService; @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { System.out.println("doGetAuthenticationInfo"); //从token获取身份(账号信息) String username = (String)token.getPrincipal(); //调用IUserService层根据账号查询出用户信息的方法 User user = userService.findUserByName(username); if(user == null){ return null; } //获取密码 String password = user.getPassword(); //获取盐 String salt = user.getSalt(); //创建认证信息对象 参数:身份 凭证 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, ByteSource.Util.bytes(salt), this.getName()); return info; } /** *认证通过以后,因为方法基本都是从后台控制器跳过来的,可以使用权限注解,将需要有权限访问的资源打上@RequiresPermissions * 注解,在SpringMVC的控制器上面打上注解以后,shiro会为控制器创建代理对象(运用aop),进行权限相关的控制 * 1、<aop:config proxy-target-class="true"/>shiro框架会创建控制器的代理对象 * 2、开启shiro的注解支持,代理对象可以拿到@RequiresPermissions注解 * 3、在底层进行权限判断,走doGetAuthenticationInfo方法, * 4、程序运行时会根据有权限判断的地方获取权限表达式和设置shiro框架权限表达式数据进行比对 * 若有权限,则可直接访问 * 若无权限,则进入没有权限访问的页面,默认没有捕获异常,可以自己处理 * 授权 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { System.out.println("doGetAuthorizationInfo"); //获取当前主身份(认证的user对象) User user = (User) principals.getPrimaryPrincipal(); //获取身份的角色的权限id String ids = user.getPermission_ids(); //切割权限id字符串获取每一个权限的id值 String[] split = ids.split(","); //创建List<Integer>.把(字符串数组split)转化成(整型数组idsList) List<Integer> idsList = new ArrayList<>(); for(String id : split){ idsList.add(Integer.valueOf(id)); } //根据每一个权限的id值获取对应的权限表达式 List<String> permissionExpressions = permissionService.selectPermissionByIds(idsList); System.out.println(permissionExpressions); //创建授权对象 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); //将查询出的权限表达式添加到权限信息对中 info.addStringPermissions(permissionExpressions); return info; } }
public class MyFormAuthenticationFilter extends FormAuthenticationFilter { /** * 记住我功能生效以后,Shiro会将用户身份数据存放到本地cookie,但是Shiro在运行过程中, * 使用session获取身份进行判断处理的,所以记住我以后会存在一个问题 * * 当用户关闭浏览器以后,再次打开,除了在过滤器中设置记住我过滤器能够访问的页面以外,/index.jsp = user * 其他的页面还是无法认证 * * 因为,Shiro从cookie中获取的信息并没有自动设置到session中,所以我们需要自己把cookie记住我信息存放到session中 */ @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { //从请求中获得Shiro的主体 Subject subject = getSubject(request, response); //从主体中获取Shiro框架的session Session session = subject.getSession(); //如果主体没有认证并且主体已经设置记住我了 if(!subject.isAuthenticated()&&subject.isRemembered()){ //获取主体的身份,从记住我的cookie中获取 User principal = (User) subject.getPrincipal(); //将身份认证信息共享到session中 session.setAttribute("user",principal); } return subject.isAuthenticated() || subject.isRemembered(); } /** * 重写FormAuthenticationFilter的onLoginSuccess方法 * 指定的url传递进去,这样就实现了跳转到指定的页面; */ @Override protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { WebUtils.getAndClearSavedRequest(request);//清理了session中保存的请求信息 WebUtils.redirectToSavedRequest(request, response, getSuccessUrl()); return false; } /** * 验证码验证 * 从request请求对象获取验证码表单数据 * 如果有:说明用户在做登录操作 要进行验证码判断 * * 判断思路: * 1、获取用户提交的验证码 * 2、从session获取共享的写入图片 随机数 * 3、比对 * 相同:放行,继续调用父类方法 * 不同:跳转到登录页,并且共享错误信息 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //只有HttpServletRequest可以获取session HttpServletRequest httpServletRequest = (HttpServletRequest) request; //从session获取验证码,正确的验证码 HttpSession session = httpServletRequest.getSession(); String validate = (String) session.getAttribute("sRand"); //获取输入的验证码 String myValidate = httpServletRequest.getParameter("verifyCode"); //验证失败,设置错误信息 if (StringUtils.isNotBlank(validate) && StringUtils.isNotBlank(myValidate) && !validate.toLowerCase().equals(myValidate.toLowerCase())) { httpServletRequest.setAttribute("errorMsg", "验证码错误"); //拒绝访问 return true; } //相同:放行,继续调用父类方法 return super.onAccessDenied(request, response); } }
/** * 管理员页面控制器 */ @Controller @RequestMapping("/admin") public class AdminController { @Autowired private IUserService userService; @Autowired private IRoleService roleService; /** * 认证失败页面,没有认证强制访问需要认证的页面自动跳转的页面 * @return */ @RequestMapping("/login") public String login(HttpServletRequest request,Model model){ //获取认证失败的错误信息,再shiro框架的FormAuthenticationFilter过滤器中共享 //共享的属性名称 shiroLoginFailure //共享shiro异常的字节码类型 String shiroLoginFailure = (String)request.getAttribute("shiroLoginFailure"); if(shiroLoginFailure != null){ if(UnknownAccountException.class.getName().equals(shiroLoginFailure)){ model.addAttribute("errorMsg","用户名错误"); }else if(IncorrectCredentialsException.class.getName().equals(shiroLoginFailure)){ model.addAttribute("errorMsg","密码错误"); } } return "forward:/login.jsp"; } @RequestMapping("/adminPage") @RequiresPermissions("admin:adminPage") public String adminPage(){ return "adminPage"; } }
用户表
角色表
权限表
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算