springboot整合shiro&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="width: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>

 

3.shiro自定義過濾器

有時候我們希望指定的請求執行特殊的過濾器,例如:

(1) shiro的過濾器鏈中增加如下策略:

FILTER_CHAIN_DEFINITION_MAP.put("/api/v1/**", "anon, api");

  以/api/v1/開頭的所有請求都不用登錄,然后會進去api攔截器。

(2)filterMaps 增加api過濾器

    // Filter工廠,設置對應的過濾條件和跳轉條件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 過濾器
        Map<String, Filter> filterMaps = new HashMap<>();
        filterMaps.put("authc", new ShiroAuthFilter());
        // 增加API接口的過濾器
        filterMaps.put("api", new ApiTokenFilter());
        shiroFilterFactoryBean.setFilters(filterMaps);

        // 定義處理規則
        shiroFilterFactoryBean.setFilterChainDefinitionMap(setFilterChainDefinitionMap());

        return shiroFilterFactoryBean;
    }

(3)API過濾器如下: 返回false則拒絕處理

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.web.filter.PathMatchingFilter;

import com.alibaba.fastjson.JSONObject;
import com.zd.bx.utils.JSONResultUtil;
import com.zd.bx.utils.shiro.APITokenUtils;
import com.zd.bx.utils.web.WebUtils;

/**
 * 驗證API接口攜帶的Token
 * 
 * @author Administrator
 *
 */
public class ApiTokenFilter extends PathMatchingFilter {

    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        if (!APITokenUtils.isValidToken(request)) {
            WebUtils.writeJsonToResponse(response, JSONObject.toJSONString(JSONResultUtil.error("訪問拒絕,無效token")));
            return false;
        }

        return true;
    }

}

 


免責聲明!

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



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