Shiro之与spring整合
Shiro与Spring整合
1.web.xml配置shiroFilter
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee">
<!-- 加载 spring 的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<!-- 配置监听器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 配置spring-MVC -->
<servlet>
<servlet-name>springmvcservlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvcservlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--DelegatingFilterProxy 作用是自动到 spring 容器查找名字为 shiroFilter(filter-name)的 bean 并把所有 Filter 的操作委托给它,ShiroFilter 需要配置到 spring 容器-->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<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>
</web-app>
2.自定义realm
public class ShiroRealm extends AuthorizingRealm {
@Override
public String getName() {
return "ShiroRealm";
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从token中 获取用户身份信息
String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
//查询数据库校验账号密码
......
//返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
username, password,getName());
return simpleAuthenticationInfo;
}
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//角色授权
Set<String> roles = new HashSet<>();
roles.add("role1");
roles.add("role2");
//资源授权
Set<String> permission = new HashSet<>();
permission.add("user_add");
permission.add("user_select");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permission);
info.setRoles(roles);
return info;
}
}
3.添加spring-shiro.xml配置文件
<!-- 配置自定义 realm -->
<bean id="userRealm" class="cn.ybzy.shiro.security.ShiroRealm"></bean>
<!-- 配置安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
</bean>
<!-- 配置Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- Shiro的核心安全接口,这个属性是必须的 -->
<property name="securityManager" ref="securityManager"/>
<!-- 登录地址 -->
<property name="loginUrl" value="/login"/>
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/nopermission"/>
<!--登陆成功-->
<property name="successUrl" value="/home"/>
<property name="filters">
<map>
//添加自定义拦截
<!-- <entry key="cross" value-ref="crossFilter"/>
<entry key="authc" value-ref="tokenUserFilter"/>-->
<!-- <entry key="authc" value-ref="formAuthenticationFilter" />-->
</map>
</property>
<!-- Shiro连接约束配置,即过滤链的定义 -->
<property name="filterChainDefinitions">
<value>
//anon 匿名访问
/login = anon
//退出拦截,请求logout.action执行退出操作
/logout = logout
//静态资源放行
/js/** anon
/images/** anon
/styles/** anon
//authc 需要认证登录
/** = authc
//roles[XX]表示有XX角色才可访问
</value>
</property>
</bean>
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
<!-- <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="username" />
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password" />
<!-- <property name="rememberMeParam" value="rememberMe"/> -->
<!-- loginurl:用户登陆地址,此地址是可以http访问的url地址 -->
<property name="loginUrl" value="/login" />
</bean>-->
4.编写登录方法
@RequestMapping("/login")
public String login(Model model, HttpServletRequest req) throws Exception{
String errorClassName = (String)req.getAttribute("shiroLoginFailure");
if(errorClassName !=null){
if(UnknownAccountException.class.getName().equals(errorClassName)) {
req.setAttribute("error", "用户名/密码错误");
} else if(IncorrectCredentialsException.class.getName().equals(errorClassName)) {
req.setAttribute("error", "用户名/密码错误");
} else if(errorClassName != null) {
req.setAttribute("error", "未知错误:" + errorClassName);
}
}
//不处理登录成功(认证成功),shiro会自动跳转到上一个请求路径
//失败会到login.jsp
req.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(req, resp);
return "forward:/login.jsp";
}
Shiro授权
一、授权方式
1.编程式:
通过写 if/else 授权代码块完成
必须进入方法中才能判断是否有权限
Subject subject = SecurityUtils.getSubject();
if(subject.hasRole(“admin”)) {
//有权限
} else {
//无权限
}
2.注解式:
通过在执行的 Java 方法上放置相应的注解完成
可以在请求进入方法之前进行权限控制
@RequiresRoles("admin")
public void hello() {
//有权限
}
没有权限将抛出相应的异常;需在spring-shiro中配置
3.JSP/GSP 标签:
虽然页面没有显式的请求按钮,但是可以通过请求地址访问
<shiro:hasRole name="admin">
<!— 有权限 —>
</shiro:hasRole>
二、配置权限注解支持
spring-mvc.xml进行如下配置:
<!-- 开启Shiro的注解 使用这些注解就需要使用 AOP 的功能来进行判断,对类进行代理-->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
权限注解
@RequiresAuthentication
表示当前 Subject 已经通过 login 进行了身份验证;即 Subject.isAuthenticated() 返回 true。
@RequiresUser
表示当前 Subject 已经身份验证或者通过记住我登录的。
@RequiresGuest
表示当前 Subject 没有身份验证或通过记住我登录过,即是游客身份。
@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
表示当前 Subject 需要角色 admin 和 user。
三、配置无权限异常信息统一处理方式
因springmvc重新定制异常处理方式所以配置的shiroFilter是无效的
<!-- 权限认证失败,则跳转到指定页面 -->
<property name="unauthorizedUrl" value="/nopermission"/>
在spring-shiro.xml中配置 无权限异常信息统一处理方式:
<!--需要处理的特殊异常,用类名或完全路径名作key,异常页作为值-->
<!-- shiro权限异常处理 -->
<bean
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 根据需要定义N多个错误异常转发 -->
//<prop key="org.apache.shiro.authz.UnauthorizedException">/nopermission</prop>
<prop key="org.apache.shiro.authz.UnauthenticatedException">redirect:/nopermission.jsp</prop>
</props>
</property>
Shiro Session管理
Shiro 提供了完整的企业级会话管理功能,不依赖于底层容器(如 web 容器 tomcat),不管 JavaSE 还是 JavaEE 环境都可以使用,提供了会话管理、会话事件监听、会话存储 / 持久化、容器无关的集群、失效 / 过期支持、对 Web 的透明支持、SSO 单点登录的支持等特性。即直接使用 Shiro 的会话管理可以直接替换如 Web 容器的会话管理。
spring-shiro.xml添加会话管理器
<!-- session管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!--<bean id="sessionManager" class="com.zhunda.upms.client.shiro.UpmsSessionManager"> -->
<!-- 设置全局会话超时时间,默认4小时(14400000) -->
<property name="globalSessionTimeout" value="14400000"/>
<!-- 是否在会话过期后会调用SessionDAO的delete方法删除会话 默认true -->
<property name="deleteInvalidSessions" value="true"/>
<!-- 会话验证器调度时间 -->
<property name="sessionValidationInterval" value="1800000"/>
<!-- session存储的实现 -->
<property name="sessionDAO" ref="redisCacheSessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
<!-- 去除URL中的JSESSIONID -->
<property name="sessionIdUrlRewritingEnabled" value="true" />
</bean>
<!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<!-- 多个项目共享JSESSIONID -->
<property name="path" value="/" />
<!-- 不会暴露给客户端 -->
<property name="httpOnly" value="true"/>
<!-- 设置Cookie的过期时间,秒为单位,默认-1表示关闭浏览器时过期Cookie -->
<property name="maxAge" value="-1"/>
<!-- Cookie名称 -->
<property name="name" value="SSO-SESSIONID"/>
</bean>
<!--</bean>-->
<!-- 会话Session ID生成器 -->
<bean id="sessionIdGenerator" class="com.zhunda.upms.client.shiro.UuidSessionIdGenerator"/>
<bean id="redisCacheSessionDAO" class="com.zhunda.upms.client.shiro.RedisCacheSessionDAO">
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean>
安全管理器securityManager引用会话管理器
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="upmsRealm"/>
<property name="sessionManager" ref="sessionManager"/>
</bean>
Shiro RememberMe
什么是RememberMe?
Shiro提供了记住我(RememberMe)的功能,访问如一些网站时,关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
1.首先在登录页面选中 RememberMe 然后登录成功;如果是浏览器登录,一般会把 RememberMe 的 Cookie 写到客户端并保存下来;
2.关闭浏览器再重新打开;会发现浏览器还是记住你的;
3.访问一般的网页服务器端还是知道你是谁,且能正常访问; 但是当我们访问一些特殊网页,如查订单或进行支付时,此时还是需要再进行身份认证的,以确保当前用户还是你。
spring-shiro.xml配置rememberMe管理器
<!-- rememberMeManager管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<property name="cookie" ref="rememberMeCookie" />
</bean>
<!-- 记住我cookie -->
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe" />
<!-- 记住我cookie生效时间30天 -->
<property name="maxAge" value="2592000" />
<property name="httpOnly" value="true"/>
</bean>
<bean id="formAuthenticationFilter"
class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="usercode" />
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password" />
<property name="rememberMeParam" value="rememberMe"/>
<!-- loginurl:用户登陆地址,此地址是可以http访问的url地址 -->
<property name="loginUrl" value="/login" />
</bean>
安全管理器securityManager引用rememberMeManager管理器
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
<property name="sessionManager" ref="sessionManager" />
<property name="cacheManager" ref="cacheManager"/>
<!-- 记住我 -->
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean>
如果要自己做 RememeberMe,需要在登录之前这样创建 Token:UsernamePasswordToken(用户名,密码,是否记住我),如:
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
subject.login(token);
Shiro缓存机制
Shiro 内部相应的组件(DefaultSecurityManager)会自动检测相应的对象(如 Realm)是否实现了 CacheManagerAware 并自动注入相应的 CacheManager。
spring-shiro.xml添加缓存管理器
<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
<property name="shared" value="true"></property>
</bean>
添加缓存配置文件 ehcache.xml
<ehcache updateCheck="false" name="shiroCache">
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="false"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
/>
</ehcache>
securityManager安全管理器引用缓存管理器
<!-- 配置安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm" />
<!--添加缓存机制-->
<property name="cacheManager" ref="cacheManager" />
</bean>
清空缓存
1.用户正常/非正常退出,缓存会自动清空
2.若变更用户权限 , 且用户不退出系统 , 但由于用户权限信息已被缓存, 修改后的权限不会立即生效.
3.修改用户权限后,用户再次登录会自动调用realm从数据库查询权限,此时用户权限才是正确的.若修改权限后想立即清除缓存,则需要调用realm中定义的clearCache方法
4.在realm中定义clearCache方法
public void clearCache(){
PrincipalCollection principals=SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}
5.在用户角色or权限发生变化时调用clearCache()清除缓存
Shiro编码加密
1.添加凭证匹配器
在spring-shiro.xml中进行如下操作:
<!-- 凭证匹配器 -->
<bean id="credentialsMatcher"
class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<!--加密算法-->
<property name="hashAlgorithmName" value="md5" />
<!--散列次数-->
<property name="hashIterations" value="2" />
</bean>
2.自定义realm引用凭证匹配器
<!-- 配置自定义 realm -->
<bean id="userRealm" class="cn.ybzy.shiro.security.ShiroRealm">
<!--使用凭证匹配器-->
<property name="credentialsMatcher" ref="credentialsMatcher" />
</bean>
3.修改自定义Realm
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从token中 获取用户身份信息
String username = (String) token.getPrincipal();
String password = new String((char[]) token.getCredentials());
//假设模拟从数据库中查询出的密码是加密后的密文 : 明文(123456) + 盐(shiro) + 散列次数(2)
String pwd ="b87dbd0bd4bcc6536a08d2027e329547";
//返回认证信息由父类AuthenticatingRealm进行认证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(
username, pwd, ByteSource.Util.bytes("shiro"),getName());
return simpleAuthenticationInfo;
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137147.html