快速搭建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 才可以訪問 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM