spring整合shiro框架的实现步骤记录
shiro
shiro是apache下的一个开源项目,我们称之为apache shiro。它是一个很易用与java项目的的安全框架,提供了认证、授权、加密、会话管理,与spring security 一样都是做一个权限的安全框架,但是与spring security 相比,在于 shiro 使用了比较简单易懂易于使用的授权方式。shiro属于轻量级框架,相对于security简单的多,也没有security那么复杂。更多详细的介绍可以从它的官网上()基本可以了解到,她主要提供以下功能:
(1)authentication(认证)
(2)authorization(授权)
(3)session management(会话管理)
(4)cryptography (加密)
首先,认证服务,也就是说通过她可以完成身份认证,让她去判断用户是否为真实的会员。
其次,授权服务,说白了就是“访问控制”服务,也就是让她来识别用户拥有哪些权限。再说的白一点,就是通过判断用户是什么角色,来赋予他哪些操作权限。
然后,还有会话管理服务, 这时一个独立的session管理框架,和我们熟知的http session 不太一样。
最后,她还提供了cryptography(加密)服务,封装了很多密码学的算法。
今天,我就不全说了,重点说下她的 会话管理功能, 其实这个也是几乎所有的web应该都会涉及到的。
在说shiro的会话管理服务前,先回顾下之前的会话管理我们是怎么做的。
1、最初我们是直接用的web服务器的 http session的机制, 也就是用户第一次进来的话,web容器会为这个请求创建一个session,然后把这个session存储起来,通过将对应的sessionid,作为cookie传给客户端,
如果客户端再次向这个服务器发送请求的话,会自动将这个sessionid带过来, 然后web服务器会根据客户端带过来的 sessionid, 判断其对于的session 是否还存在于内存中(session是有过期时间的,可以在web.xml文件里面配置),如果找不到对应的session了,说明已经过了session失效时间,这时web服务器会再次为它创建一个session,然后和之前一样,将这个新的sessionid传给客户端。
因此,我们可以通过这种机制,在程序里管理用户的登录会话,比如我们在用户第一次登录成功后,将用户的基本信息存储在session里(比如:session.setattribute("user", "userinfo")
),下次用户再次访问的时候,我们根据获取当前session里的user信息
(session.getattribute("user")
),来判断用户是否过期,如果获取不到,那么提示用户重新登录。
2、第二种方式,就是我们将存储信息的地方转移到第三方介质中,比如缓存里,memecache或者redis都可以,这种方式主要是因为分布式系统的出现而采用的。
这种情况下,就需要我们自己生成sessionid了,一般我们会用一个定义好的前缀(user:login:token
)再加上userid,或者时间戳都可以。 然后我们会将这个sessionid作为缓存的key, 用户的信息作为value,存入缓存中,并设置失效时间:
jedisclient.set(tokenkey, jsonutil.tojsonstring(userinfo)); jedisclient.expire(tokenkey, token_lose_seconds);
我们还要将生成的这个tokenkey通过cookie传到客户端: cookieutils.setcookie(request, response, "tt_token", tokenkey);
这样,我们在用户下次访问的时候(定义一个拦截器),就可以从cookie里取出对应的tokenkey,然后用这个tokenkey去到缓存里取相应的值,如果获取不到,说明这个key已经失效了,提示用户重新登录。
注: tokenkey 很重要,她是连接缓存端和客户端的枢纽。
3、最后一种就是我们shiro方式了,思路也类似,代码挺简单的,那我就直接上代码吧:
1)、新建一个 applicationcontext-shiro.xml文件:
<?xml version="1.0" encoding="utf-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd"> <bean id="shirofilter" class="org.apache.shiro.spring.web.shirofilterfactorybean"> <property name="securitymanager" ref="securitymanager"></property> <property name="loginurl" value="/loginpage"></property> <property name="unauthorizedurl" value="/pages/unauthorized.jsp"/> <property name="filterchaindefinitions"> <value> /jcaptcha* = anon /logout = anon </value> </property> </bean> <bean class="org.springframework.beans.factory.config.methodinvokingfactorybean"> <property name="staticmethod" value="org.apache.shiro.securityutils.setsecuritymanager"></property> <property name="arguments" ref="securitymanager"></property> </bean> <bean id="securitymanager" class="org.apache.shiro.web.mgt.defaultwebsecuritymanager"> <property name="cachemanager" ref="cachemanager"></property> <property name="sessionmanager" ref="sessionmanager"></property> </bean> <bean id="sessionmanager" class="org.apache.shiro.web.session.mgt.defaultwebsessionmanager"> <property name="sessiondao" ref="sessiondao"></property> </bean> <bean id="sessiondao" class="com.smart.core.shiro.mysessiondao"></bean> //这个类是需要自己实现的 <bean id="cachemanager" class="org.apache.shiro.cache.memoryconstrainedcachemanager"></bean> </beans>
2)、在web.xml 里配置相应的 filter:
<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>
3)写一个实现类,继承 abstractsessiondao,实现相应的方法。
package com.jdd.core.shiro; import com.smart.core.redis.redismanager; import org.apache.shiro.session.session; import org.apache.shiro.session.unknownsessionexception; import org.apache.shiro.session.mgt.eis.abstractsessiondao; import org.springframework.beans.factory.annotation.autowired; import org.springframework.util.serializationutils; import java.io.*; import java.util.arraylist; import java.util.collection; public class mysessiondao extends abstractsessiondao { @autowired private redismanager redismanager; @override public void update(session session) throws unknownsessionexception { redismanager.set(serializationutils.serialize(session.getid().tostring()), serializationutils.serialize(session)); redismanager.expire(serializationutils.serialize(session.getid().tostring()), 60); } @override public void delete(session session) { redismanager.del(serializationutils.serialize(session.getid().tostring())); } @override public collection<session> getactivesessions() { return new arraylist<session>(); } @override protected serializable docreate(session session) { //这就是第一次访问的时候,创建sessionid serializable sid = this.generatesessionid(session); assignsessionid(session, sid); redismanager.set(serializationutils.serialize(session.getid().tostring()), serializationutils.serialize(session)); redismanager.expire(serializationutils.serialize(session.getid().tostring()), 60); return sid; } @override protected session doreadsession(serializable serializable) { //这个方法其实就是通过sessionid读取session,每读一次,都要重新设置失效时间 byte[] aa = redismanager.get(serializationutils.serialize(serializable.tostring())); session session = (session) serializationutils.deserialize(aa); redismanager.set(serializationutils.serialize(serializable.tostring()), serializationutils.serialize(session)); redismanager.expire(serializationutils.serialize(serializable.tostring()), 60); return session; } }
4)下一步,我就是要在登录成功之后的逻辑里,获取到shiro 的session,然后将用户信息设置进去
package com.smart.controller; import com.smart.pojo.user; import com.smart.service.userservice; import org.apache.shiro.securityutils; import org.apache.shiro.mgt.securitymanager; import org.apache.shiro.subject.subject; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.beans.factory.annotation.autowired; import org.springframework.stereotype.controller; import org.springframework.ui.model; import org.springframework.web.bind.annotation.*; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; @controller @requestmapping("/user") public class usercontroller { @autowired private userservice userservice; @autowired private securitymanager sm; //注入securitymanager private logger logger = loggerfactory.getlogger(usercontroller.class); @requestmapping(value = "/loginpage") public string loginpage(){ return "user/userlogin"; } @requestmapping(value = "/userlogin", method = requestmethod.post) public string userlogin(@requestparam(value="name") string name, @requestparam(value="pwd") string pwd, model model){ logger.info("enter userlogin..."); user user = userservice.getuserbynameandpassword(name, pwd); if(user == null){ logger.info("user is not exist..."); model.addattribute("login_error", "用户名或密码错误"); return "user/userlogin"; } securityutils.setsecuritymanager(sm); subject currentuser = securityutils.getsubject(); currentuser.getsession().setattribute("login_user", user); return "redirect:/employee/list"; } }
获取当前用户,在shiro里是主题,然后获取对应的session,并将用户信息设置进去,是不是感觉有点像http session的操作的样子,哈哈。
5)、最后,定义一个springmvc 的拦截器,在拦截器里获取相应的session里的而用户信息,如果获取不到,则跳转到登录界面。
package com.smart.core.shiro; import com.smart.pojo.user; import org.apache.shiro.securityutils; import org.apache.shiro.mgt.securitymanager; import org.apache.shiro.subject.subject; import org.slf4j.logger; import org.slf4j.loggerfactory; import org.springframework.beans.factory.annotation.autowired; import org.springframework.web.servlet.handlerinterceptor; import org.springframework.web.servlet.modelandview; import javax.servlet.http.httpservletrequest; import javax.servlet.http.httpservletresponse; public class logininterceptor implements handlerinterceptor { private logger logger = loggerfactory.getlogger(logininterceptor.class); @autowired private securitymanager sm; @override public boolean prehandle(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o) throws exception { logger.info("enter logininterceptor..."); httpservletrequest request = httpservletrequest; httpservletresponse response = httpservletresponse; logger.info("request uri===>"+request.getrequesturi()); //如果是登录页面的请求,则不拦截,否则会陷入死循环 if(request.getrequesturi().contains("loginpage") || request.getrequesturi().contains("userlogin")){ return true; }else{ securityutils.setsecuritymanager(sm); subject currentuser = securityutils.getsubject(); object obj = currentuser.getsession().getattribute("login_user"); if(obj==null){ response.sendredirect("http://localhost:8080/user/loginpage"); return false; }else{ user user = (user)obj; if(user==null || user.getname()==null){ response.sendredirect("http://localhost:8080/user/loginpage"); return false; }else{ return true; } } } } @override public void posthandle(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o, modelandview modelandview) throws exception { } @override public void aftercompletion(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, object o, exception e) throws exception { } }
到这里就基本结束了,如果你现在直接访问主页信息的话,它会自动跳到登录页面。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
推荐阅读
-
spring整合shiro框架的实现步骤记录
-
spring boot整合Shiro实现单点登录的示例代码
-
spring boot整合Shiro实现单点登录的示例代码
-
spring boot整合redis实现shiro的分布式session共享的方法
-
day62 JavaWeb框架阶段—— Mybatis的注解开发&SSM框架整合(原始方式整合&Spring整合MyBatis& MyBatis的注解实现复杂映射开发)
-
Spring Cloud Alibaba整合Sentinel的实现步骤
-
整合Spring MVC,mybatis,hibernate,freemarker框架实现的自定义注解Validator验证机制实现对敏感词过滤的代码分享
-
Spring Cloud Alibaba整合Sentinel的实现步骤