zoukankan      html  css  js  c++  java
  • spring 整合 shiro框架

      shiro是用来干嘛的?从它的官网上(http://shiro.apache.org/)基本可以了解到,她主要提供以下功能:

      (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 { } }

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

  • 相关阅读:
    [LeetCode]Contains Duplicate
    C++基础之泛型算法
    KMP算法
    [LeetCode]Shortest Palindrome
    [LeetCode]House Robber
    Palindrome Linked List leetcode
    Rotate Array leetcode
    Rotate Image LeetCode
    Rotate List leetcode
    Reorder List leetcode
  • 原文地址:https://www.cnblogs.com/xiexin2015/p/9031785.html
Copyright © 2011-2022 走看看