Shiro+SpringBoot

实现密码MD5加密,挤人(单用户登录),统计在线人数,记住我和利用redis实现session缓存和共享


1.Maven

        
        
            org.apache.shiro
            shiro-core
            1.2.3
        
        
            org.apache.shiro
            shiro-spring
            1.2.3
        
        

      
      
            com.alibaba
            druid
            1.0.20
        

        
        
            org.crazycake
            shiro-redis
            2.4.2.1-RELEASE
            
                
                    shiro-core
                    org.apache.shiro
                
            
        
        

2.shiro配置文件:ShiroConfiguration.java
我将配置文件内容分为四大部分,第一部分是最基础和必要的权限控制配置,其中里面有无加密和加密的选择,第二部分是开启cookie的记住我功能,第三是开启redis,实现session共享和缓存,第四是开启shiro注解。

package com.sun.demo.shiro;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;


/*
 *这个类就相当于spring整合shiro的spring-shiro.xml中对shiro的配置。
 */
@Configuration
class ShiroConfiguration {

//1.==========================================必要,基础的权限配置==========================================
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        //设置securityManager
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);

        //设置登录页面,authc未登录时跳转的界面
        //可以写路由也可以写jsp页面的访问路径
        //如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
        shiroFilterFactoryBean.setLoginUrl("/login");
        //设置登录成功跳转的页面
        shiroFilterFactoryBean.setSuccessUrl("/pages/index.jsp");
        //设置未授权跳转的页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/pages/unauthorized.jsp");
        //定义过滤器
        LinkedHashMap filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/index", "authc");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/loginUser", "anon");
        filterChainDefinitionMap.put("/admin", "roles[admin]");
        filterChainDefinitionMap.put("/edit", "perms[delete]");
        filterChainDefinitionMap.put("/nurse", "roles[nurse]");
        filterChainDefinitionMap.put("/doctor", "roles[doctor]");
        //游客,开发权限
        filterChainDefinitionMap.put("/guest/**", "anon");
        //用户,需要角色权限 “user”
        filterChainDefinitionMap.put("/user/**", "roles[user]");
        //管理员,需要角色权限 “admin”
        filterChainDefinitionMap.put("/admin/**", "roles[admin]");
        //开放登陆接口
        filterChainDefinitionMap.put("/login", "anon");
        //其余接口一律拦截
        //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 定义安全管理器securityManager,注入自定义的realm,在这里对输入的密码和数据库密码经过realm设置的加密方式来进行对照策略(默认是相等)
     * @param userRealm
     * @return
     */
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        //将缓存注入安全管理器,就不会反复执行  realm的授权方法了;只要实现了shiro的cache接口、CacheManager接口就可以用来注入安全管理器
        //shiro自带的一个内存缓存,本质是hashmap,MemoryConstrainedCacheManager(),试验没问题,非常轻,简单的登录用这个
        defaultWebSecurityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis,nigix试验分布式,确实 做到了session共享
        defaultWebSecurityManager.setSessionManager(sessionManager());
        //注入记住我cookie管理器;
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
        return defaultWebSecurityManager;
    }


    // 无论是加密还是不加密,必选之一
    //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<不使用MD5加密

    //    @Bean(name = "userRealm")
    //    public UserRealm getUserRealm(){
    //        return new UserRealm();
    //    }

    // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<使用MD5加密 start
    @Bean("userRealm")
    public UserRealm getUserRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        UserRealm authRealm = new UserRealm();
        authRealm.setAuthorizationCachingEnabled(false);
        //设置加密格式
        authRealm.setCredentialsMatcher(matcher);
        return authRealm;
    }

    /**
     * 密码校验规则HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的 ,
     * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //指定加密方式为MD5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //加密次数
        credentialsMatcher.setHashIterations(1024);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }

    // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>使用MD5加密end


    /**
     * lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
     * (可选)我这里写了但是没用
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }




//2.===========================================开启cookie的记住我===========================================

    /**
     * cookie对象;
     * @return
     */
    public SimpleCookie rememberMeCookie(){
        System.out.println("cookie11111");
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //
        simpleCookie.setMaxAge(2592000);
        return simpleCookie;
    }

    /**
     * cookie管理对象;记住我功能
     * @return
     */
    public CookieRememberMeManager rememberMeManager(){
        System.out.println("cookieManager11111");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return cookieRememberMeManager;
    }

//3.==================================redis缓存,实现session,认证缓存==================================

    /**
     * 配置shiro redisManager
     * 网上的一个 shiro-redis 插件,实现了shiro的cache接口、CacheManager接口就
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost("localhost");
        redisManager.setPort(6379);
        redisManager.setExpire(18000);// 配置过期时间
        // redisManager.setTimeout(timeout);
        redisManager.setPassword("123456");
        return redisManager;
    }

    /**
     * Redis集群使用RedisClusterManager,单个Redis使用RedisManager
     * cacheManager 缓存 redis实现
     * 网上的一个 shiro-redis 插件
     * @return
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     * shiro session的管理
     */
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }



//4.==========================================Shiro注解 ==================================================
    /**
     * 开启Shiro注解(如@RequiresRoles,@RequiresPermissions),
     * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    /**
     * 开启aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

3.自定义realm (认证和授权) :UserRealm.java
里面授权和认证具体内容按照自己的项目进行适当更改,只是展示授权和认证形式

package com.sun.demo.shiro;

import com.sun.demo.service.Impl.UserServiceImpl;
import com.sun.demo.service.UserService;
import com.sun.demo.entity.Permission;
import com.sun.demo.entity.Role;
import com.sun.demo.entity.User;
import com.sun.demo.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.util.ByteSource;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

/**
 * 自定义realm
 */
public class UserRealm extends AuthorizingRealm{
    @Autowired
    private UserServiceImpl userService;
    /**
     * 为用户授权
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //获取前端输入的用户信息,封装为User对象
        User userweb = (User) principals.getPrimaryPrincipal();
        //获取前端输入的用户名
        String username = userweb.getUsername();
        //根据前端输入的用户名查询数据库中对应的记录
        User user = userService.findByUsername(username);
        //如果数据库中有该用户名对应的记录,就进行授权操作
        if (user != null){
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //因为addRoles和addStringPermissions方法需要的参数类型是Collection
            //所以先创建两个collection集合
            Collection rolesCollection = new HashSet();
            Collection perStringCollection = new HashSet();
            //获取user的Role的set集合
            Set roles = user.getRoles();
            //遍历集合
            for (Role role : roles){
                //将每一个role的name装进collection集合
                rolesCollection.add(role.getName());
                //获取每一个Role的permission的set集合
                Set permissionSet =  role.getPermissions();
                //遍历集合
                for (Permission permission : permissionSet){
                    //将每一个permission的name装进collection集合
                    perStringCollection.add(permission.getName());
                }
                //为用户授权
                info.addStringPermissions(perStringCollection);
            }
            //为用户授予角色
            info.addRoles(rolesCollection);
            return info;
        }else{
            return null;
        }

    }



    /**
     * 认证登录
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //=========================未加密版==========================
        //token携带了用户登录的信息
        // UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //获取前端输入的用户名
        //String username  = usernamePasswordToken.getUsername();
        //根据前端输入的用户名查询数据库中的记录
        // User user = userService.findByUsername(username);
        //校验密码,验证登录
        // return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());

        //=========================MD5加密版=========================
        //token携带了用户信息
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        //获取前端输入的用户名
        String userName  = usernamePasswordToken.getUsername();
        //根据用户名查询数据库中对应的记录
        User user = userService.findByUsername(userName);
        if (null == user) {
            throw new UnknownAccountException("未找到该用户!");
        }
        System.out.println(user.getUsername()+"用户登陆了,user = "+user.toString());


        //单用户登录,在线人数
        //处理session
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
        //获取当前已登录的用户session列表
        Collection sessions = sessionManager.getSessionDAO().getActiveSessions();
        User temp;
        int allUser=sessions.size();
        for(Session session : sessions){
            //清除该用户以前登录时保存的session,强制退出
            Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {
                continue;
            }
            temp = (User) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if(userName.equals(temp.getUsername())) {
                allUser--;
                sessionManager.getSessionDAO().delete(session);
            }
        }
        System.out.println("现在登录人数为:"+allUser);


        //当前realm对象的name
        String realmName = getName();
        //盐值
        ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());
        //封装用户信息,构建AuthenticationInfo对象并返回,里面是输入的信息和数据库信息,加密策略,盐,到安全管理器securityManager
        AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(), credentialsSalt, realmName);
        return authcInfo;
    }
}

4.增加session的监控和管理(可选,增强)

    /**
     * shiro session的管理
     */
    public DefaultWebSessionManager sessionManager() {
        Collection listeners = new ArrayList();
        listeners.add(sessionListener());
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        //我写的SessionListener没啥子大的作用,没有写太多功能增强,以后可以扩展该类
        sessionManager.setSessionListeners(listeners);
        //修改Cookie名称,由JSESSIONID改为我们定义的sid,有利于与Tomcat 、Jetty默认的JSESSIONID 冲突
        sessionManager.setSessionIdCookieEnabled(true);
        sessionManager.setSessionIdCookie(sessionIdCookie());
        //去掉shiro登录时url里的JSESSIONID,shiro版本1.3.2及以后才可以设置
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        //全局会话超时时间(单位毫秒)
        sessionManager.setGlobalSessionTimeout(60 * 30 * 1000);
        //是否开启删除无效的session对象  默认为true
        sessionManager.setDeleteInvalidSessions(true);
        //是否开启定时调度器进行检测过期session 默认为true
        sessionManager.setSessionValidationSchedulerEnabled(true);
        return sessionManager;
    }

       /**
     * 配置保存sessionId的cookie
     * 注意:这里的cookie 不是上面的记住我 cookie 记住我需要一个cookie session管理 也需要自己的cookie
     * @return
     */
    @Bean("sessionIdCookie")
    public SimpleCookie sessionIdCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("sid");
        //maxAge=-1表示浏览器关闭时才失效此Cookie
        //shiro默认会去修改cookie的Max-age=0,让浏览器1小时后把cookie删掉
        //这样用户访问我们网站就会报错:没登陆!,但实际redis里面还是存在该session,只是浏览器端对应的cookie被删了
        simpleCookie.setMaxAge(-1);
        return simpleCookie;
    }

    /**
     * 配置session监听
     * @return
     */
    @Bean("sessionListener")
    public ShiroSessionListener sessionListener(){
        ShiroSessionListener sessionListener = new ShiroSessionListener();
        return sessionListener;
    }

ShiroSessionListener.class

import org.apache.shiro.session.Session;
import org.apache.shiro.session.SessionListener;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author: wangsaichao
 * @date: 2018/5/15
 * @description: 配置session监听器
 */
public class ShiroSessionListener implements SessionListener{

    /**
     * 统计在线人数
     * juc包下线程安全自增
     */
    private final AtomicInteger sessionCount = new AtomicInteger(0);

    /**
     * 会话创建时触发
     * @param session
     */
    @Override
    public void onStart(Session session) {
        //会话创建,在线人数加一
        System.out.println("会话创建:" + session.getId());
        sessionCount.incrementAndGet();
    }

    /**
     * 退出会话时触发
     * @param session
     */
    @Override
    public void onStop(Session session) {
        //会话退出,在线人数减一
        System.out.println("会话停止:" + session.getId());
        sessionCount.decrementAndGet();
    }

    /**
     * 会话过期时触发
     * @param session
     */
    @Override
    public void onExpiration(Session session) {
        //会话过期,在线人数减一
        System.out.println("会话过期:" + session.getId());
        sessionCount.decrementAndGet();
    }
    /**
     * 获取在线人数使用
     * @return
     */
    public AtomicInteger getSessionCount() {
        return sessionCount;
    }
}

5.带盐的MD5加密代码
注册和测试时可以用

package com.sun.demo.test;

import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
import org.springframework.util.DigestUtils;

import java.math.BigInteger;
import java.security.MessageDigest;

public class MD5 {


    public static void main(String[] args) {
        String hashAlgorithName = "MD5";
        String password = "123";
        int hashIterations = 1024;//加密次数
        ByteSource credentialsSalt = ByteSource.Util.bytes("zx");

        Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);

        System.out.println(obj);

    }
}

注意
1.因为用了redis缓存session,所以相关的实体类必须可序列化 (implements Serializable)

2.配置shiro redisManager时,使用shiro-redis 插件,它所所依赖的包会和热部署的包冲突,出现从redis取对象反序列化失败,出现java.lang.ClassCastException 两个一样的类不能强转。两种解决方案:
一是放弃热部署,删掉pom.xml中相关依赖,二是添加配置文件解决(我选了这个):spring-devtools.properties,可以放在resources/META-INF下

restart.include.mapper=/mapper-[\\w-\\.]+jar
restart.include.pagehelper=/pagehelper-[\\w-\\.]+jar
# 因为我项目中引用了 org.crazycake:shiro-redis ,所以要引用下面这个配置
restart.include.shiro=/shiro-[\\w-\\.]+jar

3.Shiro的认证注解处理是有内定的处理顺序的,如果有个多个注解的话,前面的通过了会继续检查后面的,若不通过则直接返回,处理顺序依次为(与实际声明顺序无关)

4.shiro注解

    @RequiresAuthentication
  表示当前Subject已经通过login 进行了身份验证;即Subject. isAuthenticated()返回true。
 
  @RequiresUser
  表示当前Subject已经身份验证或者通过记住我登录的。
 
  @RequiresGuest
  表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。
 
  @RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)
  @RequiresRoles(value={“admin”})
  @RequiresRoles({“admin“})
  表示当前Subject需要角色admin 和user。
 
  @RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)
  表示当前Subject需要权限user:a或user:b。

5.需要注意的是,退出登录时需要调用Subject.logout()方法,该方法会自动删除redis中的session和cache缓存。

6.使用shiro-redis做Session共享后,跟踪源码发现在修改角色名称后AuthorizationInfo中的角色名称依然是修改之前的。所以就需要用户退出后重登才会更新认证信息。

7.如果说想在注册成功或者修改密码成功后,即数据库已经更新了。如果想要直接登陆当前已经注册的账号或者修改用户信息的账户,只需要利用更新后的数据库中的账号密码重新登录(密码,无论你是是否加密的,都只需要明文的,也就是用户输的密码,注意密码要加toCharArray)。
用户无需操作,也感受不到,但是其实已经重新登录过了,后台已经重新登录了。如果你使用替身,记得也要更新session中的user属性

UsernamePasswordToken token = new UsernamePasswordToken();  
token.setUsername(username);  
token.setPassword(password.toCharArray());  
SecurityUtils.getSubject().login(token); 

下面这种(也就是登录时用到的),也可以

UsernamePasswordToken token = new UsernamePasswordToken(username, password,rememberMe);
// 从SecurityUtils里边创建一个 subject
Subject subject = SecurityUtils.getSubject();
// 执行认证登陆
subject.login(token);

8.spring集成shiro后对request和session的影响
request和session 已经被shiro包装过了,封装了httpRequest和httpSession,但是操作都没啥差别。
spring整合shiro后,可以通过两种方式获取到session:

//方法里携带
public String  getUserByHttpSession(HttpSession session) {
        User user = (User) session.getAttribute("user");
        System.out.println(user.getUsername());
        return "getUserByHttpSession";
    }
//通过HttpServletRequest获取session
 Session session = request.getSession();
//通过shiro获取session
 Subject currentUser = SecurityUtils.getSubject();
 Session session = currentUser.getSession();

依旧使用request,response,session:

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

使用redis共享的session,就会由sessionDao管理,不用管有什么区别,正常使用即可
但是有点不同的是如何获取当前用户?
1.从shiro的subject获取,这是真正的用户

后台
User user = (User)SecurityUtils.getSubject().getPrincipal();

前台
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>

2.替身。你可能会发现使用上面的可能有点不方便或者不习惯,我们可以使用“替身”,在登陆时在session中增加名为user的属性,值为此用户,这个只是替身,因为session里的真正用户无法直接获取getAttribute();

// 执行认证登陆
subject.login(token);
session.setAttribute("user", subject.getPrincipal());

我们查看一下session的数据

------org.apache.shiro.subject.support.DefaultSubjectContext_AUTHENTICATED_SESSION_KEY:true--------

------shiroSavedRequest:org.apache.shiro.web.util.SavedRequest@1a13a25--------
//替身
------user:User{uid=4, username='szw', password='f1de8fd2e48e32b39701c6df0cdd4557', email='null', roles=[Role{rid=4, name='NurseDefault'}]}--------
//真正的用户
------org.apache.shiro.subject.support.DefaultSubjectContext_PRINCIPALS_SESSION_KEY:User{uid=4, username='szw', password='f1de8fd2e48e32b39701c6df0cdd4557', email='null', roles=[Role{rid=4, name='NurseDefault'}]}--------



相关知识:

为啥在shiro中使用redis来实现session共享
1.加快session查询和权限认证速度,减少服务器压力
2.当我们的用户量到达一定程度的时候,单机的服务器已经支撑不了目前的访问量,我们肯定需要对服务器做集群(分布式)来提高应用的负载能力.比如我们使用Nginx+Tomcat来实现集群,如果使用的是轮询的测试,我肯定需要涉及到Session共享的问题,解决Session共享有很多种方案,我们选择的是Shiro+Redis来实现。因为Shiro中本身就提供了sessionManager和sessionDAO,我们可以把shiro和redis集成起来,把session持久化到Redis中,然后需要使用的时候从Redis中获取对应的session.



不错的相关文章
shiro注解
SpringBoot整合Shiro,权限的动态加载、更新,Shiro-Redis实现分布式Session共享,挤人功能
springboot+shiro+redis项目整合
spring boot 集成shiro和redis(对redis更加深入的处理)
session共享
springboot整合shiro,redis缓存session
springmvc集成shiro后,session、request姓汪还是姓蒋?(很不错,发现shiro对spring的影响)
springboot整合shiro-session管理(六)(有对session更深的利用和处理)
shiro获取登陆用户和修改用户信息的方法

你可能感兴趣的:(Shiro+SpringBoot)