欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-12-17 13:08:10
shiro shiro是apache下的一个开源项目,我们称之为apache shiro。它是一个很易用与java项目的的安全框架,提供了认证、授权、加密、会话管理,与s...

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 {

 }
}

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

总结

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

上一篇:

下一篇: