您的位置:首页 > 编程语言 > Java开发

shiro与spring整合在web项目中的应用

2018-01-24 12:40 627 查看
一、思想认识

这里shiro就相当于一个拦截器,这个拦截器与spring整合在了一起,当一个URL请求的时候首先--->走shiro拦截器------>springMVC的handler函数;

二、shiro与spring项目的整合

1、shiro在web.xml中的配置

在web系统中,shiro也通过filter进行拦截。filter拦截后将操作权交给spring中配置的filterChain(过虑链儿)shiro提供很多filter。

在applicationContext-shiro.xml
中配置web.xml中fitler对应spring容器中的bean。

在web.xml中配置filter

<!-- shiro的filter -->
<!-- shiro过虑器,DelegatingFilterProxy通过代理模式将spring容器中的bean和filter关联起来 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
<!-- 设置spring容器filter的bean id,如果不设置则找与filter-name一致的bean-->
<init-param>
<param-name>targetBeanName</param-name>
<param-value>shiroFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


2、配置applicationContext-shiro.xml

<!-- web.xml中shiro的filter对应的bean -->
<!-- Shiro 的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<!-- loginUrl认证提交地址,如果没有认证将会请求此地址进行认证,请求此地址将由formAuthenticationFilter进行表单认证 -->
<property name="loginUrl" value="/login.action" />
<!-- 认证成功统一跳转到first.action,建议不配置,shiro认证成功自动到上一个请求路径 -->
<property name="successUrl" value="/first.action"/>
<!-- 通过unauthorizedUrl指定没有权限操作时跳转页面-->
<property name="unauthorizedUrl" value="/refuse.jsp" />

<!-- 过虑器链定义,从上向下顺序执行,一般将/**放在最下边 -->
<property name="filterChainDefinitions">
<value>
<!-- 对静态资源设置匿名访问 -->
/images/** = anon
/js/** = anon
/styles/** = anon
/** = authc
<!-- /** = anon所有url都可以匿名访问 -->

</value>
</property>
</bean>

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
</bean>

<!-- realm -->
<bean id="customRealm" class="cn.itcast.ssm.shiro.CustomRealm">
</bean>

</beans>


上面就是把shiro与spring的整合,与项目融合一起;
3、shiro的过虑器

 

过滤器简称

对应的java类

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表示需要认证(登录)才能使用,FormAuthenticationFilter是表单认证,没有参数

perms:例子/admins/user/**=perms[user:add:*],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,例如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法。

user:例如/admins/user/**=user没有参数表示必须存在用户,身份认证通过或通过记住我认证通过的可以访问,当登入操作时不做检查

4、jsp标签 授权

 
Jsp页面添加:
<%@ tagliburi="http://shiro.apache.org/tags" prefix="shiro" %>
 
标签名称

标签条件(均是显示标签内容)

<shiro:authenticated>

登录之后

<shiro:notAuthenticated>

不在登录状态时

<shiro:guest>

用户在没有RememberMe时

<shiro:user>

用户在RememberMe时

<shiro:hasAnyRoles name="abc,123" >

在有abc或者123角色时

<shiro:hasRole name="abc">

拥有角色abc

<shiro:lacksRole name="abc">

没有角色abc

<shiro:hasPermission name="abc">

拥有权限资源abc

<shiro:lacksPermission name="abc">

没有abc权限资源

<shiro:principal>

显示用户身份名称

 <shiro:principal property="username"/>     显示用户身份中的属性值
 
修改itemsList.jsp页面:



接下来就是针对在一个项目中使用shiro对用户进行  认证和授权

三、shiro在web开发中的应用

1、登录认证

涉及到JSP页面传参到request域中到shiro拦截器中,然后拿着传进来的参数与realm域中的数据库主题参数进行对比的一个过程;

2、shiro缓存

针对授权频繁查询数据库,需要使用shiro缓存;

hiro中提供了对认证信息和授权信息的缓存。shiro默认是关闭认证信息缓存的,对于授权信息的缓存shiro默认开启的。主要研究授权信息缓存,因为授权的数据量大。

 

用户认证通过。

该 用户第一次授权:调用realm查询数据库

该 用户第二次授权:不调用realm查询数据库,直接从缓存中取出授权信息(权限标识符)。

通常使用ehcache来与shiro进行整合;





1.1.1.1 配置cacheManager

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--diskStore:缓存数据持久化的目录 地址  -->
<diskStore path="F:\develop\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>


之后把给缓存配置在shiro的安全管理器中;

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
</bean>

<!-- 缓存管理器 -->
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:shiro-ehcache.xml"/>
</bean>


3、 缓存清空

如果用户正常退出,缓存自动清空。

 

如果用户非正常退出,缓存自动清空。

 

如果修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。

需要手动进行编程实现:

在权限修改后调用realm的clearCache方法清除缓存。

下边的代码正常开发时要放在service中调用。

在service中,权限修改后调用realm的方法。

在realm中定义clearCached方法:

//清除缓存
public void clearCached() {
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
super.clearCache(principals);
}


4、 sessionManager

和shiro整合后,使用shiro的session管理,shiro提供sessionDao操作
会话数据。

在shiro的安全管理器中配置shiro的session管理

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager" />

</bean>

<!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<!-- session的失效时长,单位毫秒 -->
<property name="globalSessionTimeout" value="600000"/>
<!-- 删除失效的session -->
<property name="deleteInvalidSessions" value="true"/>
</bean>


5、 验证码

1.1.1 思路

注意的是:验证码是在JSP页面的输入数据与实际生成的放在http的session中的进行对比的,发生在用
e5ae
户主题的数据和与realm中的用户主题的数据对比之前;

shiro使用FormAuthenticationFilter进行表单认证,验证校验的功能应该加在FormAuthenticationFilter中,在认证之前进行验证码校验。

 

需要写FormAuthenticationFilter的子类,继承FormAuthenticationFilter,改写它的认证方法,在认证之前进行验证码校验。

 自定义FormAuthenticationFilter

public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

//原FormAuthenticationFilter的认证方法
@Override
protected boolean onAccessDenied(ServletRequest request,
ServletResponse response) throws Exception {
//在这里进行验证码的校验

//从session获取正确验证码
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpSession session =httpServletRequest.getSession();
//取出session的验证码(正确的验证码)
String validateCode = (String) session.getAttribute("validateCode");

//取出页面的验证码
//输入的验证和session中的验证进行对比
String randomcode = httpServletRequest.getParameter("randomcode");
if(randomcode!=null && validateCode!=null && !randomcode.equals(validateCode)){
//如果校验失败,将验证码错误失败信息,通过shiroLoginFailure设置到request中
httpServletRequest.setAttribute("shiroLoginFailure", "randomCodeError");
//拒绝访问,不再校验账号和密码
return true;
}
return super.onAccessDenied(request, response);
}

}

在applicationContext-shiro.xml中配置自定义的拦截器

<!-- 自定义filter配置 -->
<property name="filters">
<map>
<!-- 将自定义 的FormAuthenticationFilter注入shiroFilter中-->
<entry key="authc" value-ref="formAuthenticationFilter" />
</map>
</property>


<!-- 自定义form认证过虑器 -->
<!-- 基于Form表单的身份验证过滤器,不配置将也会注册此过虑器,表单中的用户账号、密码及loginurl将采用默认值,建议配置 -->
<bean id="formAuthenticationFilter"
class="cn.itcast.ssm.shiro.CustomFormAuthenticationFilter ">
<!-- 表单中账号的input名称 -->
<property name="usernameParam" value="username" />
<!-- 表单中密码的input名称 -->
<property name="passwordParam" value="password" />
<!-- 记住我input的名称 -->
<property name="rememberMeParam" value="rememberMe"/>
</bean>

通过shiro的拦截器验证后进入到springMVC的handler函数login.action

@Controller
public class LoginController {

@Autowired
private SysService sysService;

//用户登陆提交方法
/**
*
* <p>Title: login</p>
* <p>Description: </p>
* @param session
* @param randomcode 输入的验证码
* @param usercode 用户账号
* @param password 用户密码
* @return
* @throws Exception
*/
/*@RequestMapping("/login")
public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{

//校验验证码,防止恶性攻击
//从session获取正确验证码
String validateCode = (String) session.getAttribute("validateCode");

//输入的验证和session中的验证进行对比
if(!randomcode.equals(validateCode)){
//抛出异常
throw new CustomException("验证码输入错误");
}

//调用service校验用户账号和密码的正确性
ActiveUser activeUser = sysService.authenticat(usercode, password);

//如果service校验通过,将用户身份记录到session
session.setAttribute("activeUser", activeUser);
//重定向到商品查询页面
return "redirect:/first.action";
}*/

//登陆提交地址,和applicationContext-shiro.xml中配置的loginurl一致
@RequestMapping("login")
public String login(HttpServletRequest request)throws Exception{
//shiro在认证过程中,如果出现认证错误,将异常类路径通过request返回
//如果登陆失败从request中获取认证异常信息,shiroLoginFailure就是shiro异常类的全限定名
String exceptionClassName = (String) request.getAttribute("shiroLoginFailure");
//根据shiro返回的异常类路径判断,抛出指定异常信息
if(exceptionClassName!=null){
if (UnknownAccountException.class.getName().equals(exceptionClassName)) {
//最终会抛给异常处理器
throw new CustomException("账号不存在");
} else if (IncorrectCredentialsException.class.getName().equals(
exceptionClassName)) {
throw new CustomException("用户名/密码错误");
} else if("randomCodeError".equals(exceptionClassName)){
throw new CustomException("验证码错误 ");
}else {
throw new Exception();//最终在异常处理器生成未知错误
}
}
//此方法不处理登陆成功(认证成功),shiro认证成功会自动跳转到上一个请求路径
//登陆失败还到login页面
return "login";
}

/*	//用户退出
@RequestMapping("/logout")
public String logout(HttpSession session)throws Exception{

//session失效
session.invalidate();
//重定向到商品查询页面
return "redirect:/first.action";

}*/

}


6、记住我的配置
这个功能一般是在登录的页面,勾选出自动登录或者是记住用户名密码之后,下次登录时不用再输入用户名和密码;

也即是:用户登陆选择“自动登陆”本次登陆成功会向cookie写身份信息,下次登陆从cookie中取出身份信息实现自动登陆。

6.1、要求:

向cookie记录身份信息需要用户身份信息对象实现序列化接口,如下

public class ActiveUser implements java.io.Serializable{}}

在shiro的安全管理器中配置 记住我的功能;

<!-- securityManager安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="customRealm" />
<!-- 注入缓存管理器 -->
<property name="cacheManager" ref="cacheManager"/>
<!-- 注入session管理器 -->
<property name="sessionManager" ref="sessionManager" />
<!-- 记住我 -->
<property name="rememberMeManager" ref="rememberMeManager"/>

</bean>


<!-- rememberMeManager管理器,写cookie,取出cookie生成用户信息 -->
<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">
<!-- rememberMe是cookie的名字 -->
<constructor-arg value="rememberMe" />
<!-- 记住我cookie生效时间30天 -->
<property name="maxAge" value="2592000" />
</bean>


注意:这个记住我的JSP页面传的参数与shiro的配置文件中的rememberMe

<!-- rememberMe是cookie的名字 -->
<constructor-arg value="rememberMe" />

要一致,这个直接走shiro进行cookie保存在了,浏览器中;



项目信息源码:点击打开链接
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shiro