zoukankan      html  css  js  c++  java
  • 快速搭建Spring Boot + Apache Shiro 环境

    个人博客网:https://wushaopei.github.io/    (你想要这里多有)

    一、Apache Shiro 介绍及概念

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

    Shiro 框架图:

    框架图解析:

    Primary Concerns四部分: 用户识别、授权、会话管理、加密

    Supporting Features 五部分: Shiro特有的API、缓存、 并发、测试、Run As 、Remember Me

    Shiro 概念层 架构:

    应用代码经过 Subject 进行简单授权和认证, Subject 又委托给SecurityManager

    二、Apache Shiro 部分功能讲解:

    1、Apache Shiro授权讲解

    • 首先调用Subject 进行处理,再委托给 Security Manageer ,Security Manageer会委托给 Authorizer ,Authorizer是真正的授权者。

    Shiro 的授权主要包含四个核心:主体、资源、权限、角色

    主体: 访问应用的用户,用户只有授权后才能访问到相应的资源

    资源: 查看、编辑某个数据,读取某个文本等都是资源

    权限: 建立在资源以及操作上,只限定能做什么

    角色: 才是真正可以对主体的权限进行分配的核心

    2、 Apache Shiro权限拦截框架图:

    3、Apache Shiro会话管理框架图

    会话管理主要是管理所有会话的创建、查询、交互等。

    三、基于SpringBoot的Apache Shiro环境快速搭建与配置

    1、环境搭建及使用

    主要涉及两点:

    • 快速搭建Spring Boot + Apache Shiro 环境
    • 常用Case实现

    (1)创建springboot 工程 Spring-boot_Shiro ,整合 Shiro 依赖

    <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-spring</artifactId>
       <version>1.4.0</version>
    </dependency>
    <dependency>
       <groupId>org.apache.shiro</groupId>
       <artifactId>shiro-core</artifactId>
       <version>1.4.0</version>
    </dependency>
    <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>druid</artifactId>
       <version>1.0.20</version>
    </dependency>
    <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.4</version>
    </dependency>
    <dependency>
       <groupId>org.apache.tomcat.embed</groupId>
       <artifactId>tomcat-embed-jasper</artifactId>
    </dependency>
    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>javax.servlet-api</artifactId>
    </dependency>
    <dependency>
       <groupId>javax.servlet</groupId>
       <artifactId>jstl</artifactId>
    </dependency>

    (2)创建数据库表结构 : user 、role、permission、permission_role、user_role、五张表;

    CREATE TABLE `user` (
    `uid`  int(11) NOT NULL AUTO_INCREMENT ,
    `username`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
    `password`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
    PRIMARY KEY (`uid`)
    )
    ENGINE=InnoDB
    DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
    AUTO_INCREMENT=3
    ROW_FORMAT=DYNAMIC;
    
    CREATE TABLE `role` (
    `rid`  int(11) NOT NULL AUTO_INCREMENT ,
    `rname`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
    PRIMARY KEY (`rid`)
    )
    ENGINE=InnoDB
    DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
    AUTO_INCREMENT=3
    ROW_FORMAT=DYNAMIC;
    
    CREATE TABLE `permission` (
    `pid`  int(11) NOT NULL AUTO_INCREMENT ,
    `name`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' ,
    `url`  varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' ,
    PRIMARY KEY (`pid`)
    )
    ENGINE=InnoDB
    DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
    AUTO_INCREMENT=1
    ROW_FORMAT=DYNAMIC;
    
    CREATE TABLE `permission_role` (
    `rid`  int(11) NOT NULL ,
    `pid`  int(11) NOT NULL ,
    INDEX `idx_rid` (`rid`) USING BTREE ,
    INDEX `idx_pid` (`pid`) USING BTREE 
    )
    ENGINE=InnoDB
    DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
    ROW_FORMAT=DYNAMIC;
    
    CREATE TABLE `user_role` (
    `uid`  int(11) NOT NULL ,
    `rid`  int(11) NOT NULL ,
    INDEX `idx_uid` (`uid`) USING BTREE ,
    INDEX `idx_pid` (`rid`) USING BTREE 
    )
    ENGINE=InnoDB
    DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
    ROW_FORMAT=DYNAMIC;

    (3)创建 user、role、permission 三个类

    public class User {
    
        private String username;
        private Integer uid;
        private String password;
        private Set<Role> roles = new HashSet<>();
        。。。。。。。。。。。。
    }
    public class Role {
    
        private Integer rid;
        private String rname;
        private Set<Permission> permissionSet = new HashSet<>();
        ...................
    }
    
    public class Permission {
    
        private Integer pid;
        private String name;
        private String url;
        。。。。。。。。。。。。。。
    }

    (4)创建service业务层、mapper 持久层

    service 、serviceimpl业务接口及实现类

    public interface UserService {
    
        User findByUsername(String username);
    }
    
    @Service
    @Transactional(readOnly=true)
    public class UserServiceimpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
        @Override
        public User findByUsername(String username) {
            return userMapper.findByUsername(username);
        }
    }

    UserMapper.java 持久层

    @Component
    @Mapper
    public interface UserMapper {
    
        User findByUsername(@Param("username") String username);
    
        List<User> getAll();
    }

    UserMapper.xml

    <mapper namespace="com.webcode.springboot.mappers.UserMapper">
    
        <resultMap id="userMap" type="com.webcode.springboot.entities.User">
            <id property="uid" column="uid"/>
            <result property="username" column="username"/>
            <result property="password" column="password"/>
            <collection property="roles" ofType="com.webcode.springboot.entities.Role">
                <id property="rid" column="rid"/>
                <result property="rname" column="rname"/>
                <collection property="permissionSet" ofType="com.webcode.springboot.entities.Permission">
                    <id property="pid" column="pid"/>
                    <result property="name" column="name"/>
                    <result property="url" column="url"/>
                </collection>
            </collection>
        </resultMap>
    
        <select id="findByUsername" parameterType="string" resultMap="userMap">
            SELECT u.*,r.*,p.*
            from user u
              INNER JOIN user_role ur on ur.uid = u.uid
                INNER JOIN role r on r.rid = ur.rid
              INNER JOIN permission_role pr on pr.rid = r.rid
              INNER JOIN permission p on pr.pid = p.pid
            WHERE u.username = #{username}
        </select>
    
        <select id="getAll"
                resultMap="userMap">
            select *
            from   USER
        </select>
    </mapper>

    (5)配置mapper.xml 包扫描位置及对应的javabean 所在包,用于mapper.xml的映射

    application.yml 配置文件:

    mybatis:
      mapper-locations: classpath*:/mybatis/mappers/*Mapper.xml #包扫描 mapper.xml 类
      type-aliases-package: com.webcode.springboot.entities  # 指定相应 mapper.xml 中实体 javabean 的所在位置
    server:
      port: 8081
    

    Bootstrap 启动类:

    //扫描Mapper接口
    @MapperScan("com.webcode.springboot.mappers")
    @ComponentScan({"com.webcode.springboot"})
    @SpringBootApplication
    public class Bootstrap {
       
       public static void main(String[] args) {
          SpringApplication.run(Bootstrap.class, args);
       }
    }

    2、基于Apache Shiro权限管理Case -- 认证授权配置

    创建自定义的 AuthRealm 类,该类 继承 AuthorizingRealm 类,并重写doGetAutherizationInfo(PrincipalCollection principals) doGetAuthenticationInfo(AuthenticationToken token) 两个方法 分别 作为授权和认证登录所用;

    public class AuthRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            // 授权要先从 session 取出对象来
            User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
            List<String> permissionList = new ArrayList<>();
            Set<Role> roleSet = user.getRoles();
            if (CollectionUtils.isNotEmpty(roleSet)){
                for (Role role : roleSet) {
                    Set<Permission> permissionSet = role.getPermissionSet();
                    if (CollectionUtils.isNotEmpty(permissionSet)){
                        for (Permission permission : permissionSet) {
                            permissionList.add(permission.getName());
                        }
                    }
                }
            }
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addStringPermissions(permissionList);
            return null;
        }
    
        // 认证登录
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; //将Token 降为 usernamePasswordToken
            String username = usernamePasswordToken.getUsername();    //去除其中的username
            User user = userService.findByUsername(username);       //根据username 查询到 user 信息
            return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
        }
    }

    3、基于Apache Shiro权限管理Case -- 密码规则校验器

    说明

    因为密码是直接取得数据库的密码,所以想要保证用户传入的密码能和我的密码能够匹配,需要创建自定义的密码校验规则。

    具体:通过重写CredentialsMatcher 类,继承SimpleCredentialsMatcher类,重新定义 密码校验规则的重写

    /**
     * @ClassName CredentialMatcher 密码校验规则的重写
     * @Description TODO
     * @Author wushaopei
     * @Date 2019/9/24 10:07
     * @Version 1.0
     */
    public class CredentialMatcher extends SimpleCredentialsMatcher {
    
        @Override
        public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
            UsernamePasswordToken token1 = (UsernamePasswordToken) token;
            String passsword = new String(token1.getPassword());
            String dbPassword = (String) info.getCredentials();
            return this.equals(passsword,dbPassword);
        }
    }

    4、基于Apache Shiro权限管理Case -- Shiro配置

    创建 ShiroConfiguration 类 ,该类是 shiro 的配置类

    @Configuration
    public class ShiroConfiguration {
    
        @Bean("shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager){
    
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    
            // 必须设置 SecurityManager
            bean.setSecurityManager(manager);
            // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            bean.setLoginUrl("/login");
            // 登录成功后要跳转的链接
            bean.setSuccessUrl("/index");
            // 未授权界面;
            bean.setUnauthorizedUrl("/unauthorized");
    
            // 拦截器.
            LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            filterChainDefinitionMap.put("/index","authc");
            filterChainDefinitionMap.put("/login","anon");
            bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return bean;
        }
        //配置核心安全事务管理器
        @Bean("securityManager")
        public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm){
    
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 设置realm.
            securityManager.setRealm(authRealm);
            return securityManager;
        }
        //配置自定义的权限登录器
        @Bean("authRealm")
        public AuthRealm authRealm(@Qualifier("credentialMatcher")CredentialMatcher matcher){
            AuthRealm authRealm = new AuthRealm();
            authRealm.setCredentialsMatcher(matcher); //给出自定密码比较器
            return authRealm;       //返回自定Realm
        }
        //配置自定义的密码比较器
        @Bean("credentialMatcher")  //生成 密码校验规则的Bean
        public CredentialMatcher credentialMatcher(){
            return new CredentialMatcher(); //返回密码校验规则实例
        }
        /**
         * 开启shiro aop注解支持. 使用代理方式;所以需要开启代码支持; Controller才能使用@RequiresPermissions
         *
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
            advisor.setSecurityManager(securityManager);
            return advisor;
        }
    
        @Bean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
            creator.setProxyTargetClass(true);
            return creator;
        }
    }

    5、定义请求接口:包括首页、登录页、异常页面

    @RequestMapping("/login")
    public String login(){
        return "login";
    }
    
    @RequestMapping("/index")
    public String index(){
        return "index";
    }
    
    @RequestMapping("/loginUser")
    public String loginUser(@RequestParam("username")String username,
                            @RequestParam("password") String password,
                            HttpSession session){
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            User principal = (User) subject.getPrincipal();
            session.setAttribute("user",principal);
            return  "index";
        }catch (Exception e){
            return "login";
        }
    }

    6、定义jsp 页面,包括index.jsp、login.jsp、

    jsp 相关配置在 yml 文件中,申明jsp的前缀、后缀

    spring:
      mvc:  ## jsp ##
        view:
          prefix: /pages/
          suffix: .jsp
    

    login.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Login</title>
    </head>
    <body>
    <h1>欢迎登录</h1>
    <form action="/loginUser" method="post">
        <input type="text" name="username"><br>
        <input type="password" name="password"><br>
        <input type="submit" value="提交"><br>
    </form>
    </body>
    </html>

    index.jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>index</title>
    </head>
    <body>
    <h1>欢迎登录, ${user.username}</h1>
    </body>
    </html>

    接口登录测试:

    提交,认证通过

    7、基于Apache Shiro权限管理Case -- Shiro配置 登录拦截

    shiro 过滤器添加配置:

       filterChainDefinitionMap.put("/loginUser","anon"); //登陸不用登陸認證
       filterChainDefinitionMap.put("/**","user"); //其他接口要驗證是否登陸過用戶

    测试:

    除了登录功能不需要进行认证外,其他接口请求都需要进行用户是否登录的认证.

    8、基于Apache Shiro权限管理Case -- Shiro配置 角色认证

    在 ShiroConfiguration.java 进行配置:

    filterChainDefinitionMap.put("/admin","roles[admin]"); //授权过程中认证角色为admin的用户才可以访问

    在AuthRealm.java进行配置:

        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            // 授权要先从 session 取出对象来
            User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next();
            List<String> permissionList = new ArrayList<>();
            List<String> roleNameList = new ArrayList<>();        //角色授权拦截。。。。。。。
            Set<Role> roleSet = user.getRoles();
            if (CollectionUtils.isNotEmpty(roleSet)){
                for (Role role : roleSet) {
                    roleNameList.add(role.getName());            //角色授权拦截。。。。。。。。
                    Set<Permission> permissionSet = role.getPermissionSet();
                    if (CollectionUtils.isNotEmpty(permissionSet)){
                        for (Permission permission : permissionSet) {
                            permissionList.add(permission.getName());
                        }
                    }
                }
            }
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            simpleAuthorizationInfo.addStringPermissions(permissionList);
            simpleAuthorizationInfo.addRoles(roleNameList);     //角色授权拦截。。。。。。
            return simpleAuthorizationInfo;
        }

    8、基于Apache Shiro权限管理Case -- Shiro配置 permission 认证

    说明:具有指定 permission 功能的permission才可以访问,如有edit时,才可以访问,否则不能访问

    在ShiroConfiguration.java类中 shiroFilter 方法中配置:

            filterChainDefinitionMap.put("/edit","perms[edit]");//具有edit 的pemission 才可以访问
    
  • 相关阅读:
    crt key转p12, jks p12互转,windows生成jks,
    使用c语言实现在linux下的openssl客户端和服务器端编程
    AES CFB/OFB/ECB/CBC/CTR优缺点
    SSL握手通信详解及linux下c/c++ SSL Socket代码举例
    SSL握手通信详解及linux下c/c++ SSL Socket代码举例(另附SSL双向认证客户端代码)
    对称加密和分组加密中的四种模式(ECB、CBC、CFB、OFB)
    Mosquitto服务器的搭建以及SSL/TLS安全通信配置
    openssl详解
    使用 openssl 生成证书
    字符编码的故事:ASCII,GB2312,Unicode,UTF-8,UTF-16
  • 原文地址:https://www.cnblogs.com/wushaopei/p/11681408.html
Copyright © 2011-2022 走看看