Springboot整合Shiro后对登录session超时自动跳转登录页、异地登录提醒、权限控制的使用

目录

一、Shiro

Shiro简介

Shiro功能

二、前期工作

创建库表

引入依赖

三、整合Shiro

自定义UserRealm

配置ShiroConfig

自定义LoginFilter

公共方法common.js

用户登录login.js

用户登录逻辑层

shiro标签说明

shiro标签使用


一、Shiro

Shiro简介

        Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

Shiro功能

        三个核心组件:Subject, SecurityManager 和 Realms.

Subject

        主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到 SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;

SecurityManager

        安全管理器;即所有与安全有关的操作都会与 SecurityManager 交互;且它管理着所有 Subject;可以看出它是 Shiro 的核心,它负责与后边介绍的其他组件进行交互,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;

Realm

        域,Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。

也就是说对于我们而言,最简单的一个 Shiro 应用:

  1. 应用代码通过 Subject 来进行认证和授权,而 Subject 又委托给 SecurityManager;
  2. 我们需要给 Shiro 的 SecurityManager 注入 Realm,从而让 SecurityManager 能得到合法的用户及其权限进行判断。

从以上也可以看出,Shiro 不提供维护用户 / 权限,而是通过 Realm 让开发人员自己注入。

二、前期工作

创建库表

  • 用户表
CREATE TABLE `sys_user`  (
  `id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `account` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `nickname` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `sex` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `telephone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `address` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
  • 角色表
CREATE TABLE `sys_role`  (
  `id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
  • 权限表
CREATE TABLE `sys_permission`  (
  `id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限名称',
  `desc` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '权限描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC;
  • 用户角色表
CREATE TABLE `sys_user_role`  (
  `id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `role_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = FIXED;
  • 角色权限表
CREATE TABLE `sys_role_permission`  (
  `id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `role_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  `permission_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = MyISAM CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = FIXED;

引入依赖

<!-- shiro整合springboot所需相关依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- 兼容于thymeleaf的shiro -->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>
        <!--end.......-->

三、整合Shiro

自定义UserRealm

//TODO 自定义的UserRealm,继承AuthorizingRealm,
//TODO 覆写doGetAuthorizationInfo、doGetAuthenticationInfo方法
public class UserRealm extends AuthorizingRealm {

    @Resource
    private UserInfoDao userInfoDao;

    @Resource
    private PermissionDao permissionDao;

    @Resource
    private RoleDao roleDao;

    // 授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        System.out.println("执行了=>授权doGetAuthorizationInfo");
        // 获取当前用户登录信息
        SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
        // 创建SimpleAuthorizationInfo对象
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        // 查询当前用户所拥有角色
        List<SysRole> roleList = roleDao.findRoleByUserId(user.getId());
        Set<String> roleSet = new HashSet<>();
        List<String> roleIds = new ArrayList<>();
        for (SysRole role : roleList) {
            // 用户角色集合
            roleSet.add(role.getRole());
            // 用户角色id集合
            roleIds.add(role.getId());
        }
        // 放入用户角色信息
        authorizationInfo.setRoles(roleSet);
        // 根据当前用户拥有的角色查询所拥有权限
        List<String> permissionList = permissionDao.findByRoleId(roleIds);
        // 放入用户权限信息
        authorizationInfo.setStringPermissions(new HashSet<>(permissionList));
        return authorizationInfo;
    }

    // 认证
    @SneakyThrows
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("执行了=>认证doGetAuthorizationInfo");
        // 获取当前用户登录信息
        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
        // 根据用户名查询用户信息
        SysUser sysUser = userInfoDao.selectByAccount(userToken.getUsername());
        if (sysUser ==null) {//没有此用户
            return null;//抛出异常 UnknownAccountException,账号不存在。
        }
        // AES加密密码
        //String password = AESUtils.decrypt(userInfo.getPassword(), secret);
        // 密码认证,shiro做
        return new SimpleAuthenticationInfo(sysUser, sysUser.getPassword(),getName());
    }
}

配置ShiroConfig

package com.example.animalhome.shiro;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import javax.servlet.Filter;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    //1. 创建realm对象,需要自定义类
    @Bean
    public UserRealm userRealm() {
        return new UserRealm();
    }

    //2. 配置ShiroDialect,用于Shiro和thymeleaf标签配合使用
    //若不配置,可能导致前端使用shiro标签后无效果
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    // 配置sessionDAO
    @Bean(name = "sessionDAO")
    public MemorySessionDAO getMemorySessionDAO() {
        return new MemorySessionDAO();
    }

    //3. shiroFilterFactoryBean
    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 设置安全管理器
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        // 添加自己的过滤器并且取名为loginFilter
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        // 自定义loginFilter,解决账号不能异地登录,以及登录失效等问题
        filterMap.put("loginFilter", new LoginFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断,anon放开,不会拦截,authc会拦截
        //登录用到的url
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/captchaImage", "anon");
        filterChainDefinitionMap.put("/publish/options", "anon");
        //静态资源不能被拦截
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/images/**", "anon");
        // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        // <!-- loginFilter:所有url都必须登录系统才可以访问-->
        filterChainDefinitionMap.put("/**", "loginFilter,authc");

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        //访问的是后端url地址为 /login的接口,/未登录页面,会跳转到登录页面
        shiroFilterFactoryBean.setLoginUrl("/login");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    //配置shiro session 的一个管理器
    @Bean(name = "sessionManager")
    public DefaultWebSessionManager getDefaultWebSessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        // 设置session过期时间,30分钟=1800000毫秒
        sessionManager.setGlobalSessionTimeout(60*60*1000);
        // 将sessionDAO放进来
        sessionManager.setSessionDAO(getMemorySessionDAO());
        return sessionManager;
    }

    //4. DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 关联userRealm
        securityManager.setRealm(userRealm);
        // 将sessionManager放进来
        securityManager.setSessionManager( getDefaultWebSessionManager() );
        // 关闭shiro的记住我验证
        securityManager.setRememberMeManager(null);
        return securityManager;
    }

    /**
     * 开启shiro aop注解支持.
     * 使用代理方式;所以需要开启代码支持;
     *
     * @return org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("getDefaultWebSecurityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}

自定义LoginFilter

        自定义LoginFilter是为了解决用户登录session超时后跳转登录页,以及异地登录提醒问题;代码中已做具体注释。

public class LoginFilter extends FormAuthenticationFilter {

    //TODO /druid为Druid监控页面,无需拦截直接访问。
    private static final String[] filter = { "/druid" };

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest hsRequest = (HttpServletRequest) request;
        HttpServletResponse hsResponse = (HttpServletResponse) response;
        String url = hsRequest.getRequestURI();

        // 不需要过滤的请求地址以及文件
        for (String str : filter) {
            if (url.contains(str) || url.equalsIgnoreCase("/")){
                return true;
            }
        }
        //这里是获取用户登录信息
        Subject subject = getSubject(request, response);  
        String account = hsRequest.getHeader("account");//获取登录账号
        // 如果没有获取到用户信息,将退出到登陆界面
        if (null == subject.getPrincipal()) {
            boolean isAjaxRequest = false;
            if(!StringUtils.isBlank(hsRequest.getHeader("x-requested-with")) &&
                    hsRequest.getHeader("x-requested-with").equals("XMLHttpRequest")){
                isAjaxRequest = true;
            }
            // 如果是Ajax返回指定数据
            if (isAjaxRequest) {
                //可以通过code403判断是否重定向,也可以自定义一个属性指定是session超时的重定向
                hsResponse.setHeader("sessionstatus", "TIMEOUT");	//返回特定数据(头部信息)
                //首先account是从前端cookie中获取,失效时间与shiro登录账号的session过期时间一致,
                //若为空说明账号登录有效时间已过,或者用户关闭浏览器,亦或者用户清空缓存等情况,均返回到登录首页,提示用户登录信息已失效。
                if (StringUtils.isNotEmpty(account)){
                    //若不为空,则从缓存中拿到是否异地登录,remoteLogin=2则是已异地登录,需返回登录界面。
                    String remoteLogin = RedisUtil.getCacheString(account);
                    if (StringUtils.isNotEmpty(remoteLogin) && StringUtils.equals(remoteLogin,"2")){
                        //首页登陆地址,并提示用户账号已在异地登录
                        hsResponse.setHeader("content_path", "/animalHome/login?strangeLand=1");
                    }
                } else {
                    //首页登陆地址,并提示用户登录信息已失效
                    hsResponse.setHeader("content_path", "/animalHome/login?strangeLand=2");
                }
                hsResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
            } else {	// 不是Ajax进行重定向处理
                hsResponse.sendRedirect("/animalHome/login?strangeLand=2");	//重定向到登陆界面
            }
            return false;
        }
        return true;
    }
}

公共方法common.js

        若用户登录session超时,异地登录时,点击页面会触发ajax请求,后端LoginFilter做出响应处理后传值给前端,由前端判断sessionstatus === "TIMEOUT"决定跳转登录首页。

//设置session超时跳转页面start
if(typeof jQuery != 'undefined') {
    //重写ajax
    $.ajaxSetup( {
        beforeSend: function(xhr) {
            xhr.setRequestHeader("account", getCookie("account"));
        },
        //设置ajax请求结束后的执行动作
        complete :
            function(XMLHttpRequest, textStatus) {
                // 通过XMLHttpRequest取得响应头,sessionstatus
                var sessionstatus = XMLHttpRequest.getResponseHeader("sessionstatus");
                if (sessionstatus === "TIMEOUT") {
                    var win = window;
                    while (win != win.top) {
                        win = win.top;
                    }
                    win.location.href = XMLHttpRequest.getResponseHeader("content_path");
                }
            }
    });
}
//设置session超时跳转页面end

用户登录login.js

 login(){
    const _this =this;
    $.ajax({
        url: getRootPath()+"/login",
        contentType:'application/json',
        type: "POST",
        data: JSON.stringify({
            "account": this.loginForm.account,
            "pass": this.loginForm.pass,
            "uuid": this.loginForm.uuid,
            "code": this.loginForm.code,
        }),
        success: function (data) {
            //重置loading登录状态。
            _this.loading = false;
            if (data.code === "0000"){
                _this.loginForm.uuid = "";
                //重点:与shiro用户登录后的session失效时间需保持一致。
                setCookie("account", _this.loginForm.account,30 * 60 * 1000);
                window.location.href = getRootPath()+"/homePage?account="+ _this.loginForm.account;
                //登录成功后重置表单字段
                _this.$refs["loginForm"].resetFields();
            } else {
                _this.loginForm.code = "";
                _this.resetImg();
                _this.$message.error(data.message);
            }
        },
    });
},

created(){
    const _this = this;
    _this.resetImg();
    const strangeLand = getUrlParam("strangeLand");
    if (strangeLand != null) {
        const message = strangeLand === "1" ? "此账号已在异地登录,若不是本人登录请及时修改密码!" : "您的登录信息已失效,请重新登录!";
        // 解决用户刷新页面不再重复弹窗问题。
        if (window.name == ""){
            window.name = "isReload";
            this.$message({
                showClose: true,
                message: message,
                type: 'warning',
                duration: 0
            });
        }
    }
},

用户登录逻辑层

/**
     * 用户登录
     * @param jsonObject 用户信息
     * @return Map
     */
    @Override
    public Map<String, Object> login(JSONObject jsonObject) throws Exception {
        Map<String, Object> loginMap = new HashMap<>();
        String code = RedisUtil.getCacheString(jsonObject.getString("uuid"));
        if(StringUtils.isEmpty(code)){
            // 验证码失效
            throw new ServiceException(RetResult.setResponse(RetCode.CODE_INVALID.getCode()));
        }
        String account = jsonObject.getString("account");
        String pass = jsonObject.getString("pass");
        String inputCode = jsonObject.getString("code");
        try {
            if(StringUtils.equals(inputCode,code)){
                //获取一个用户
                Subject subject = SecurityUtils.getSubject();
                //封装用户的登录数据
                UsernamePasswordToken token = new UsernamePasswordToken(account, pass);
                //登录验证
                subject.login(token);
                Collection<Session> sessions = sessionDAO.getActiveSessions();
                if (subject.isAuthenticated()) {
                    Optional<Session> loginedUser = sessions.stream().filter(session -> account.equals(session.getAttribute("loginedUser"))).findFirst();
                    if (loginedUser.isPresent()){
                        //TODO 方法一、当第二次登录时,给出提示“用户已登录”,停留在登录页面
                        //TODO 弊端:如果操作人员不是常规退出,而是通过浏览器清除全部的浏览数据,刷新页面虽然返回登录页面,
                        //TODO 但是再用刚才的账号登录时会提示已经登录,因为之前的账号shiro还是一直认证通过状态,
                        //TODO 只能等session超时了才能登录(如果设置session超时时间为2小时或更久那就太悲剧了)
//                        subject.logout();
//                        throw new RuntimeException("用户已登录");
                        //TODO 方法二、当第二次登录时,把第一个session剔除
                        //TODO 弊端:虽然不会产生方法一的问题,但是两个人AB公用一个账户,AB同时登录,AB同时处在下图的页面,
                        //TODO A修改了demo的状态为锁定,而B用户的页面不会改变仍是正常的状态。
                        Session session = loginedUser.get();
                        session.setTimeout(0);
                    }
                }
                //若当前只有一个用户登录,则在redis缓存存入account=1,并设置过期时间,
                //当另一个用户再登录账号时,缓存会取到数据,此时将account更改为2(代表
                //此账号已存在异地登录);这时若前者再操作系统,会结合上方已将前者登录session
                //剔除,综合两种情况跳转至登录首页,并提示用户密码需修改。
                String remoteLogin = StringUtils.isNotEmpty(RedisUtil.getCacheString(account)) ? "2" : "1";
                //重点:与前端login.js中用户登录后,存入cookie中的account失效时间需保持一致。
                RedisUtil.setCacheObject(account,remoteLogin,60*30,TimeUnit.SECONDS);
                //设置过期时间为30分钟,单位毫秒,30分钟=1800000毫秒
                subject.getSession().setTimeout(1000*60*30);
                shiroUtils.getSession().setAttribute("loginedUser",account);
            }else{
                //验证码错误
                RetResult.setResponse(RetCode.CODE_FAILED.getCode(),loginMap);
            }
        } catch (UnknownAccountException e) {
            //账号不存在
            RetResult.setResponse(RetCode.ACCOUNT_NOT_EXIST.getCode(),loginMap);
        } catch (DisabledAccountException e) {
            //账号被禁用
            RetResult.setResponse(RetCode.ACCOUNT_IS_DISABLED.getCode(),loginMap);
        } catch (IncorrectCredentialsException e) {
            //账号或密码错误
            RetResult.setResponse(RetCode.INCORRECT_CREDENTIALS.getCode(),loginMap);
        } catch (Throwable e) {
            //系统异常
            RetResult.setResponse(RetCode.SYS_ERROR.getCode(),loginMap);
        }
        return loginMap;
    }

shiro标签说明

标签

含义

shiro:principal

当前用户的登录信息,用户名之类

shiro:guest=""

验证是否是游客,即未认证的用户

shiro:user=""

验证是否是已认证或已记住用户

shiro:authenticated=""

验证是否是已认证用户,不包括已记住用户

shiro:notAuthenticated= “”

未认证用户,但是 已记住用户

shiro:lacksRole=“admin”

表示没有 admin 角色的用户

shiro:hasRole=“admin”

表示拥有 admin 角色的用户

shiro:hasAllRoles=“admin, user1”

表示需要同时拥有两种角色

shiro:hasAnyRoles=“admin, user1”

表示 拥有其中一个角色即可

shiro:lacksPermission=“admin:delete”

类似于 shiro:lacksRole

shiro:hasPermission=“admin:delete”

类似于 shiro:hasRole

shiro:hasAllPermissions=“admin:delete, admin:edit”

类似于 shiro:hasAllRoles

shiro:hasAnyPermission=“admin:delete, admin:edit”

类似于 hasAnyRoles

shiro标签使用

html引入shiro 的命名空间:xmlns:shiro=http://www.pollix.at/thymeleaf/shiro

根据自己需求使用相应的shiro标签

principal标签:输出当前用户信息,通常为登录帐号信息。

Hello, <shiro:principal/>, how are you today?

guest标签:验证当前用户是否为“访客”,即未认证(包含未记住)的用户。

<shiro:guest> 
	Hi there!  Please <a href="login.jsp">Login</a> or <a href="signup.jsp">Signup</a> today! 
</shiro:guest>

user标签:认证通过或已记住的用户。

<shiro:user> 
    Welcome back John!  Not John? Click <a href="login.jsp">here<a> to login. 
</shiro:user>

authenticated标签:已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。

<shiro:authenticated> 
    <a href="updateAccount.jsp">Update your contact information</a>. 
</shiro:authenticated>

notAuthenticated标签:未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。

<shiro:notAuthenticated> 
    Please <a href="login.jsp">login</a> in order to update your credit card information. 
</shiro:notAuthenticated>

lacksRole标签:与hasRole标签逻辑相反,当用户不属于该角色时验证通过。

<shiro:lacksRole name="administrator"> 
    Sorry, you are not allowed to administer the system. 
</shiro:lacksRole>

<!-- 第二种写法,嵌入Element前端框架 -->
<el-submenu index="2" shiro:lacksRole="admin"></el-submenu> 

hasRole标签:验证当前用户是否属于该角色。

<shiro:hasRole name="administrator"> 
    <a href="admin.jsp">Administer the system</a> 
</shiro:hasRole>

<!-- 第二种写法,嵌入Element前端框架 -->
<el-submenu index="2" shiro:hasRole="admin"></el-submenu>  

hasAnyRole标签 :验证当前用户是否属于以下任意一个角色。

<shiro:hasAnyRoles name="developer, project manager, administrator"> 
    You are either a developer, project manager, or administrator. 
</shiro:hasAnyRoles>

<!-- 第二种写法,嵌入Element前端框架 -->    
<el-submenu index="2" shiro:hasAnyRoles="admin,user"></el-submenu> 

hasPermission标签 :验证当前用户是否拥有指定权限。

<shiro:hasPermission name="user:create"> 
    <a href="createUser.jsp">Create a new User</a> 
</shiro:hasPermission>

<!-- 第二种写法,嵌入Element前端框架 -->    
<el-submenu index="2" shiro:hasPermission="'admin:add'"></el-submenu>

lacksPermission标签 :与hasPermission标签逻辑相反,当前用户没有指定权限时,验证通过。

<shiro:lacksPermission name="user:create"> 
    <a href="createUser.jsp">Create a new User</a> 
</shiro:lacksPermission>

hasAllRoles标签:当前用户同时拥有多个角色权限时,验证通过。

<shiro:hasAllRoles name="admin,user"> 
    <a href="createUser.jsp">Create a new User</a> 
</shiro:hasAllRoles>

<!-- 第二种写法,嵌入Element前端框架 -->   
<el-submenu index="2" shiro:hasAllRoles="admin,user"></el-submenu>

hasAllPermissions标签:当前用户同时拥有多个指定权限时,验证通过。

<shiro:hasAllPermissions name="admin:add,user:add"> 
    <a href="createUser.jsp">Create a new User</a> 
</shiro:hasAllPermissions>

<!-- 第二种写法,嵌入Element前端框架 -->   
<el-submenu index="2" shiro:hasAllPermissions="'admin:add,user:add'"></el-submenu>

hasAnyPermission标签:验证用户是否拥有属于以下任意一个权限。

<shiro:hasAnyPermissions name="admin:add,user:add"> 
    <a href="createUser.jsp">Create a new User</a> 
</shiro:hasAnyPermissions>

<!-- 第二种写法,嵌入Element前端框架 -->    
<el-submenu index="2" shiro:hasAnyPermissions="'admin:add,user:add'"></el-submenu>

  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用 Spring Boot 整合 Shiro 进行开发时,可以通过配置 Shirosession 过期时间来实现会话超时。具体实现步骤如下: 1. 在 Shiro 的配置文件中配置 session 过期时间: ```xml <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <!-- session 过期时间,单位毫秒 --> <property name="globalSessionTimeout" value="1800000"/> <!-- 是否开启删除无效的 session 对象 --> <property name="deleteInvalidSessions" value="true"/> </bean> ``` 2. 在 Spring Boot 的配置类中配置 Shiro 过滤器: ```java @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 必须设置 SecurityManager shiroFilterFactoryBean.setSecurityManager(securityManager); // 配置登录的 url 和登录成功的 url shiroFilterFactoryBean.setLoginUrl("/login"); shiroFilterFactoryBean.setSuccessUrl("/index"); // 配置未授权跳转shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // 配置访问权限 LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); // 过滤链定义,从上向下顺序执行,一般将 /** 放在最为下边 // authc:所有 url 都必须认证通过才可以访问;anon:所有 url 都都可以匿名访问 filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("/logout", "logout"); filterChainDefinitionMap.put("/static/**", "anon"); filterChainDefinitionMap.put("/js/**", "anon"); filterChainDefinitionMap.put("/css/**", "anon"); filterChainDefinitionMap.put("/images/**", "anon"); filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } @Bean public DefaultWebSecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userRealm()); securityManager.setSessionManager(sessionManager()); return securityManager; } @Bean public UserRealm userRealm() { return new UserRealm(); } @Bean public DefaultWebSessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); // 设置 session 过期时间,单位毫秒 sessionManager.setGlobalSessionTimeout(1800000); // 删除无效的 session 对象 sessionManager.setDeleteInvalidSessions(true); return sessionManager; } } ``` 通过以上配置,可以在 Shiro 中实现 session 会话超时功能。在用户登录后,session过期时间会被设置为配置的时间,当用户在该时间内没有操作时,session 会话将被 Shiro 销毁,用户需要重新登录

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值