Java 企业级权限管理项目笔记(三) - - -Apache Shiro 权限管理框架

一、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 才可以访问

猜你喜欢

转载自www.cnblogs.com/wushaopei/p/11681408.html