• springboot整合shiro


      关于shiro的简介与使用方法在shiro分类中已经使用过了,而且在spring中已经成功的整合了shiro。下面研究springboot+thymeleaf中使用shiro。

      spring整合shiro参考:https://www.cnblogs.com/qlqwjy/p/7257502.html

      springboot整合shiro实际上是将xml整合的方式转为Java配置方式。

      基于SpringDataJPA。

    1.springboot整合shiro

    1.系统权限需要的五张表(三个bean,SpringdataJPA自动创建中间表)

    package cn.qlq.shiro.bean;
    
    import javax.persistence.Entity;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Entity
    @Table(name = "system_shiro_permission")
    public class Permission {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
    
        private String name; // 权限名 唯一
    
        private String url; // 访问地址信息 唯一
    
        private String description; // 描述信息
    
      // get,setter
    }
    package cn.qlq.shiro.bean;
    
    import java.util.List;
    
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name = "system_shiro_role")
    public class Role {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
    
        private String name; // 角色名 唯一
    
        private String description; // 描述信息
    
        @OneToMany(fetch = FetchType.EAGER)
        private List<Permission> permissions; // 一个用户角色对应多个权限
    
        // getter,setter
    
    }
    package cn.qlq.shiro.bean;
    
    import java.util.List;
    
    import javax.persistence.Entity;
    import javax.persistence.FetchType;
    import javax.persistence.GeneratedValue;
    import javax.persistence.GenerationType;
    import javax.persistence.Id;
    import javax.persistence.OneToMany;
    import javax.persistence.Table;
    
    @Entity
    @Table(name = "system_shiro_user")
    public class ShiroUser {
    
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
    
        private String username;// 用户名 唯一
        private String password;// 用户密码
    
        @OneToMany(fetch = FetchType.EAGER)
        private List<Role> roles;// 用户角色 一个用户可能有一个角色,也可能有 多个角色
    
    // getter,setter
    
    }

    2. pom文件增加如下配置:

            <!-- 整合shiro -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.3.2</version>
            </dependency>

    3.mapper层采用SpringDataJPA自动创建的接口实现

    package cn.qlq.shiro.mapper;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import cn.qlq.shiro.bean.Permission;
    
    public interface PermissionMapper extends JpaRepository<Permission, Integer> {
        Permission findByName(String name);
    }
    package cn.qlq.shiro.mapper;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import cn.qlq.shiro.bean.Role;
    
    public interface RoleMapper extends JpaRepository<Role, Integer> {
        Role findByName(String name);
    }
    package cn.qlq.shiro.mapper;
    
    import org.springframework.data.jpa.repository.JpaRepository;
    
    import cn.qlq.shiro.bean.ShiroUser;
    
    public interface ShiroUserMapper extends JpaRepository<ShiroUser, Integer> {
        ShiroUser findByUsername(String username);
    
        ShiroUser findByUsernameAndPassword(String username, String password);
    }

    4.自定义Realm和Shiro配置

    package cn.qlq.shiro;
    
    import javax.annotation.PostConstruct;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    import cn.qlq.shiro.bean.Permission;
    import cn.qlq.shiro.bean.Role;
    import cn.qlq.shiro.bean.ShiroUser;
    import cn.qlq.shiro.mapper.ShiroUserMapper;
    
    @Component
    public class UserAuthRealm extends AuthorizingRealm {
    
        @Autowired
        private ShiroUserMapper shiroUserMapper;
    
        /**
         * 权限核心配置 根据数据库中的该用户 角色 和 权限
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            ShiroUser user = (ShiroUser) principals.getPrimaryPrincipal();
            for (Role role : user.getRoles()) { // 获取 角色
                authorizationInfo.addRole(role.getName()); // 添加 角色
                for (Permission permission : role.getPermissions()) { // 获取 权限
                    authorizationInfo.addStringPermission(permission.getName());// 添加
                                                                                // 权限
                }
            }
    
            return authorizationInfo;
        }
    
        /**
         * 用户登陆凭证信息
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String username = (String) token.getPrincipal();
            ShiroUser user = shiroUserMapper.findByUsername(username);
            if (user == null) {
                return null;
            }
    
            AuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), this.getName());
            return authenticationInfo;
        }
    
        // 清除缓存
        public void clearCache() {
            PrincipalCollection principalCollection = SecurityUtils.getSubject().getPrincipals();
            super.clearCache(principalCollection);
        }
    }
    package cn.qlq.shiro;
    
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import cn.qlq.shiro.bean.Permission;
    import cn.qlq.shiro.mapper.PermissionMapper;
    
    @Configuration
    public class ShiroConfig {
    
        @Autowired
        private PermissionMapper permissionMapper;
    
        @Autowired
        private UserAuthRealm userAuthRealm;
    
        /**
         * 配置 资源访问策略 . web应用程序 shiro核心过滤器配置
         */
        @Bean
        public ShiroFilterFactoryBean factoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
            factoryBean.setSecurityManager(securityManager);
            factoryBean.setLoginUrl("/shiro/login.html");// 登录页
            // 首页(这个不需要设置,因为是JS登录之后自己重定向)
            // factoryBean.setSuccessUrl("/shiro/index.html");
            factoryBean.setUnauthorizedUrl("/shiro/unauthorized.html");// 未授权界面;
    
            // 自定义filter配置( 配置 拦截过滤器链)
            factoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap());
            return factoryBean;
        }
    
        /**
         * 配置 SecurityManager,可配置一个或多个realm
         */
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(userAuthRealm);
            // securityManager.setRealm(xxxxRealm);
            return securityManager;
        }
    
        /**
         * 开启shiro 注解支持. 使以下注解能够生效 : 需要认证
         * {@link org.apache.shiro.authz.annotation.RequiresAuthentication
         * RequiresAuthentication} 需要用户
         * {@link org.apache.shiro.authz.annotation.RequiresUser RequiresUser} 需要访客
         * {@link org.apache.shiro.authz.annotation.RequiresGuest RequiresGuest}
         * 需要角色 {@link org.apache.shiro.authz.annotation.RequiresRoles
         * RequiresRoles} 需要权限
         * {@link org.apache.shiro.authz.annotation.RequiresPermissions
         * RequiresPermissions}
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
        /**
         * 配置 拦截过滤器链. map的键 : 资源地址 ; map的值 : 所有默认Shiro过滤器实例名 默认Shiro过滤器实例 参考 :
         * {@link org.apache.shiro.web.filter.mgt.DefaultFilter}
         */
        private Map<String, String> setFilterChainDefinitionMap() {
            Map<String, String> filterMap = new LinkedHashMap<>();
            // 注册 数据库中所有的权限 及其对应url
            List<Permission> allPermission = permissionMapper.findAll();// 数据库中查询所有权限
            for (Permission p : allPermission) {
                filterMap.put(p.getUrl(), "perms[" + p.getName() + "]"); // 拦截器中注册所有的权限
            }
            filterMap.put("/static/**", "anon"); // 公开访问的资源
            filterMap.put("/shiro/doLogin.html", "anon"); // 登录地址放开
            filterMap.put("/logout", "logout"); // 配置登出页,shiro已经帮我们实现了跳转
            filterMap.put("/**", "authc"); // 所有资源都需要经过验证
            return filterMap;
        }
    
    }

    5.控制层代码和登录代码

    package cn.qlq.shiro.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    @RequestMapping("shiro")
    public class ShiroIndexController {
    
        @RequestMapping("login")
        public String login() {
            return "shiro/login";
        }
    
        @RequestMapping("index")
        public String index() {
            return "shiro/index";
        }
    
        @RequestMapping("unauthorized")
        public String unauthorized() {
            return "shiro/unauthorized";
        }
    
    }
    package cn.qlq.shiro.controller;
    
    import java.util.Date;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import cn.qlq.shiro.bean.ShiroUser;
    import cn.qlq.shiro.mapper.ShiroUserMapper;
    import cn.qlq.utils.JSONResultUtil;
    
    @Controller
    @RequestMapping("shiro")
    public class ShiroLoginController {
    
        @Autowired
        private ShiroUserMapper shiroUserMapper;
    
        @RequestMapping("doLogin")
        @ResponseBody
        public JSONResultUtil<String> doLogin(String username, String password, HttpServletRequest request) {
            ShiroUser user = shiroUserMapper.findByUsernameAndPassword(username, password);
            if (user == null) {
                return new JSONResultUtil<>(false, "账号或者密码错误");
            }
    
            // shiro中进行登录
            Subject currentUser = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            currentUser.login(token);
    
            // 设置登录的user
            HttpSession session = request.getSession();
            session.setAttribute("user", user);
    
            return new JSONResultUtil<>(true, "ok");
        }
    }

    6. 前台登录界面和首页(thymeleaf界面+layui框架)

    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="UTF-8"/>
        <title>后台登录-X-admin2.0</title>
        <meta name="renderer" content="webkit|ie-comp|ie-stand"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
        <meta http-equiv="Cache-Control" content="no-siteapp" />
    
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="stylesheet" th:href="${#httpServletRequest.getContextPath()+'/static/x-admin/css/font.css'}"/>
        <link rel="stylesheet" th:href="${#httpServletRequest.getContextPath()+'/static/x-admin/css/xadmin.css'}"/>
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/js/jquery.min.js'}"></script>
        <script th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/lib/layui/layui.js'}" charset="utf-8"></script>
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/js/xadmin.js'}"></script>
        
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/MyJs/shiro/login.js'}"></script>
    </head>
    <body class="login-bg">
        <div class="login layui-anim layui-anim-up">
            <div class="message">x-admin2.0-管理登录(Shiro)</div>
            <div id="darkbannerwrap"></div>
            
            <form method="post" class="layui-form" >
                <input name="username" placeholder="用户名" value="admin"  type="text" lay-verify="required" class="layui-input" />
                <hr class="hr15"/>
                <input name="password" lay-verify="required" placeholder="密码" value="admin"  type="password" class="layui-input"/>
                <hr class="hr15"/>
                <input value="登录" lay-submit="xx" lay-filter="login" style="100%;" type="submit" />
                <hr class="hr20"/>
            </form>
        </div>
    </body>
    </html>

    登录JS代码login.js

        $(function() {
            layui.use('form', function(){
              var form = layui.form;
              // layer.msg('玩命卖萌中', function(){
              //   //关闭后的操作
              //   });
              //监听提交
              form.on('submit(login)', function(data){
                  //打印一下填写的值然后区后台进行登陆
                $.post("/shiro/doLogin.html",data.field,function(result){
                    if(result!=null && result.success == true){
                        window.location = "/shiro/index.html";
                    }else{
                        layer.msg(result.msg);
                    }
                },'json');
                return false;
              });
            });
        })

    首页index.html代码:

    <!doctype html>
    
    <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
          
    <head>
        <meta charset="UTF-8"/>
        <title>后台登录-X-admin2.0</title>
        <meta name="renderer" content="webkit|ie-comp|ie-stand"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <meta name="viewport" content="width=device-width,user-scalable=yes, minimum-scale=0.4, initial-scale=0.8,target-densitydpi=low-dpi" />
        <meta http-equiv="Cache-Control" content="no-siteapp" />
        
    
        <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
        <link rel="stylesheet" th:href="${#httpServletRequest.getContextPath()+'/static/x-admin/css/font.css'}"/>
        <link rel="stylesheet" th:href="${#httpServletRequest.getContextPath()+'/static/x-admin/css/xadmin.css'}"/>
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/js/jquery.min.js'}"></script>
        <script th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/lib/layui/layui.js'}" charset="utf-8"></script>
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/js/xadmin.js'}"></script>
        
        
        <script type="text/javascript" th:src="${#httpServletRequest.getContextPath()+'/static/x-admin/MyJs/login.js'}"></script>
    
    </head>
    <body class="login-bg">
    首页
    </body>
    </html>

    2.thymeleaf整合Shiro标签

    1.pom文件引入

            <dependency>
                <groupId>com.github.theborakompanioni</groupId>
                <artifactId>thymeleaf-extras-shiro</artifactId>
                <version>1.2.1</version>
            </dependency>

    2.ShiroConfig里增加如下bean

        @Bean
        public ShiroDialect shiroDialect() {
            return new ShiroDialect();
        }

    3.thymeleaf首页改动如下:

    <html lang="zh_CN" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">

    4.页面正常使用shiro标签

        <span shiro:authenticated="false" >
              <span>欢迎您:<span th:text="${session.user.username}"></span></span>
        </span>
        <shiro:hasRole name="usermanager">
            系统管理员
        </shiro:hasRole>
        
        <shiro:hasPermission name="user:add">
            有增加权限
        </shiro:hasPermission>
        
        <shiro:hasPermission name="user:update">
            有修改权限
        </shiro:hasPermission>
  • 相关阅读:
    k8s系列---service
    算法
    golang-练习ATM --面向对象实现
    golang-练习ATM
    k8s系列---pod介绍
    12.20 一组v-if/v-else-if/v-else 的元素类型相同,应该使用 key
    12.20 await 操作符的学习(await后跟非promsie、promsie(成功/失败)的几种情况测试)
    12.20 async关键字的学习
    12.20 falsy变量
    12.19 js中递归优化(递归爆栈)
  • 原文地址:https://www.cnblogs.com/qlqwjy/p/12409361.html
走看看 - 开发者的网上家园