当前位置: 移动技术网 > IT编程>开发语言>Java > spring整合shiro框架的实现步骤记录

spring整合shiro框架的实现步骤记录

2019年07月19日  | 移动技术网IT编程  | 我要评论

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 {

 }
}

到这里就基本结束了,如果你现在直接访问主页信息的话,它会自动跳到登录页面。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对移动技术网的支持。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网