在SpringBoot中使用Shiro作權(quán)限驗證

pom文件導入


        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- shiro ehcache -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-web</artifactId>
            <version>1.4.0</version>
        </dependency>

Shiro權(quán)限配置主類


import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 描述:Shiro權(quán)限配置
 */
@Configuration
public class ShiroConfig {

    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問題瞎饲。
     * 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的肛炮,以為在
     * 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
     *
     Filter Chain定義說明
     1裸扶、一個URL可以配置多個Filter榨为,使用逗號分隔
     2、當設置多個過濾器時献联,全部驗證通過劝篷,才視為通過
     3、部分過濾器可指定參數(shù)通今,如perms粥谬,roles
     *
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager") SecurityManager manager){

        System.out.println("  。辫塌。漏策。。shirFilter臼氨。掺喻。。储矩。感耙。。 ");

        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(manager);
        // 配置登錄的url和登錄成功的url

//        bean.setLoginUrl("/static/html/login.html");
//        bean.setSuccessUrl("/home");

        // 定義過濾器
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("authc", new ShiroLoginFilter());
        filterMap.put("perms", new ShiroPermissionsFilter());
        bean.setFilters(filterMap);

        // 配置訪問權(quán)限
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // 配置不同的URL采用的驗證方式
        // anon表示可以匿名訪問
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/info", "authc,perms");// 表示需要認證才可以訪問
//        filterChainDefinitionMap.put("/**", "authc,perms");// 表示需要認證才可以訪問
//        filterChainDefinitionMap.put("/*.*", "authc,perms");
        bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return bean;
    }

    // 配置核心安全事務管理器
    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm) {
        System.err.println("--------------shiro已經(jīng)加載----------------");
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(userRealm);
//        manager.setSessionManager(sessionManager);
//        manager.setCacheManager();
        return manager;
    }


    // 配置自定義的權(quán)限登錄器
    @Bean(name = "userRealm")
    public UserRealm userRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(matcher);
        return userRealm;
    }

    // 配置自定義的密碼匹配比較器
    @Bean(name = "credentialsMatcher")
    public CredentialsMatcher credentialsMatcher() {
        return new CredentialsMatcher();
    }
}

密碼匹配

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;

/**
 * 
 * 描述:密碼匹配比較
 */
public class CredentialsMatcher extends SimpleCredentialsMatcher {

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {

        System.out.println("....密碼的比對....");


        UsernamePasswordToken utoken = (UsernamePasswordToken) token;
        // 獲得用戶輸入的密碼:(可以采用加鹽(salt)的方式去檢驗)
        String inPassword = new String(utoken.getPassword());
        // 獲得數(shù)據(jù)庫中的密碼
        String dbPassword = (String) info.getCredentials();
        // 進行密碼的比對
        return this.equals(inPassword, dbPassword);
    }

}

用戶


/**
 * 描述:用戶
 */
public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

登錄及授權(quán)

import org.apache.shiro.authc.*;
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 java.util.ArrayList;
import java.util.List;

/**
 *
 * 描述:登錄及授權(quán)
 */
public class UserRealm extends AuthorizingRealm {

    // 認證.登錄 提供賬戶信息返回認證信息
    /**
     * 當調(diào)用如下代碼時會進入這個方法(一般是登錄頁面)
     * Subject currentUser = SecurityUtils.getSubject();
     * currentUser.login(token);
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
            throws AuthenticationException {

        UsernamePasswordToken utoken = (UsernamePasswordToken) authenticationToken; // 獲取用戶輸入的token
        System.out.println(".....認證.登錄....");

        //根據(jù)用戶名去數(shù)據(jù)庫查詢相關(guān)記錄持隧,并返回驗證
        String username = utoken.getUsername();
        //
        User user = new User();
        user.setUsername(username);
        user.setPassword("123456");

        if (user == null) {
            return null;
        }
        return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName());
    }

    // 授權(quán) 角色
//    提供用戶信息返回權(quán)限信息
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        // 獲取session中的用戶
        User user = (User) principalCollection.fromRealm(this.getClass().getName()).iterator().next();

        System.out.println();
        System.out.println(".....授權(quán)....");
        System.out.println();

        List<String> permissions = new ArrayList<>();
        permissions.add("abc1");
        permissions.add("abc2");
//        Set<Role> roles = user.getRoles();
//        for (Role role : roles) {
//            Set<Module> modules = role.getModules();
//            for (Module module : modules) {
//                permissions.add(module.getCode());
//            }
//        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addStringPermissions(permissions); // 將權(quán)限放入shiro中.
        return info;
    }

}

判斷是否已經(jīng)登錄


import org.apache.shiro.web.servlet.AdviceFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * 判斷是否登錄的過濾器即硼,被Shiro組件調(diào)用使用
 */
public class ShiroLoginFilter  extends AdviceFilter {

    /**
     * 在訪問controller前判斷是否登錄,返回json屡拨,不進行重定向只酥。
     *
     * @param request
     * @param response
     * @return true-通過驗證褥实,false-驗證失敗
     * @throws Exception
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;

        User User = (User) httpServletRequest.getSession().getAttribute("user");

        httpServletRequest.getSession().setMaxInactiveInterval(36000);
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json");
        httpServletResponse.setHeader("Access-Control-Allow-Origin", "*");
        httpServletResponse.setHeader("Access-Control-Allow-Credentials", "true");
        httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        httpServletResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with");
        PrintWriter printWriter = null;
        if (null == User) {
            try {
                printWriter = response.getWriter();
                printWriter.write("{\"errorCode\": \"未登錄\"}");
                printWriter.flush();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (printWriter != null) {
                    printWriter.close();
                }
            }
            return false;
        }
        return true;
    }
}

判斷是否有權(quán)限


import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 *
 * 描述:驗證是否有權(quán)限的過濾器
 */
public class ShiroPermissionsFilter extends PermissionsAuthorizationFilter {


    @Override
    public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws IOException {

        // 記錄日志
        Subject subject = this.getSubject(request, response);

        /**獲取用戶信息,可以根據(jù)用戶信息查看有沒有對應模塊的權(quán)限*/
        User user = (User) subject.getPrincipal();

        //true有權(quán)限; false沒有權(quán)限
        boolean isPermitted = false;

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;


        String code = httpServletRequest.getParameter("cmd");

        //寫死一個權(quán)限作為測試
        if(code.equals("100"))//如何cmd=100就有權(quán)限
            isPermitted = true;

        return isPermitted;
    }

    /**
     * shiro認證perms資源失敗后回調(diào)方法
     *
     * @param servletRequest
     * @param servletResponse
     * @return
     * @throws IOException
     */
    @Override
    protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {
        PrintWriter printWriter = null;
        try {
            servletResponse.setCharacterEncoding("UTF-8");
            servletResponse.setContentType("application/json");
            printWriter = servletResponse.getWriter();
            HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;


            printWriter.write("{\"error\": \"沒有權(quán)限\"}");


            printWriter.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (printWriter != null) {
                printWriter.close();
            }
        }
        return false;
    }
}

Controller


import com.szcentral.conf.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by cgz on 2018-04-08 14:11
 * 描述:
 */
@RestController
public class SampleController {
    @RequestMapping("/")
    String home() {
        return "Hello World!";
    }

    @RequestMapping("/info")
    public Map getMap(){
        Map map = new HashMap();
        map.put("name", "chen");
        map.put("age", "22");
        map.put("tel", "13682452210");
        return map;
    }

    @RequestMapping("/login")
    public String login(HttpSession session,
                        HttpServletRequest request,
                        HttpServletResponse response){
        System.out.println("...接收登錄Controller裂允。损离。。绝编。");
        //添加用戶認證信息
        Subject subject = SecurityUtils.getSubject();
        String username = "chen";
        String pwd = "123456";
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, pwd);
        //進行驗證草冈,這里可以捕獲異常,然后返回對應信息
        subject.login(usernamePasswordToken);


        User user = (User) subject.getPrincipal();
        session.setAttribute("user", user);

        return "login";
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓮增,一起剝皮案震驚了整個濱河市怎棱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绷跑,老刑警劉巖拳恋,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異砸捏,居然都是意外死亡谬运,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門垦藏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梆暖,“玉大人,你說我怎么就攤上這事掂骏『洳担” “怎么了?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵弟灼,是天一觀的道長级解。 經(jīng)常有香客問我,道長田绑,這世上最難降的妖魔是什么勤哗? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮掩驱,結(jié)果婚禮上芒划,老公的妹妹穿的比我還像新娘。我一直安慰自己欧穴,他們只是感情好民逼,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著苔可,像睡著了一般缴挖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焚辅,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天映屋,我揣著相機與錄音苟鸯,去河邊找鬼。 笑死棚点,一個胖子當著我的面吹牛早处,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瘫析,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼砌梆,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了贬循?” 一聲冷哼從身側(cè)響起咸包,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎杖虾,沒想到半個月后烂瘫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡奇适,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年坟比,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嚷往。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡葛账,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出皮仁,到底是詐尸還是另有隱情籍琳,我是刑警寧澤,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布魂贬,位于F島的核電站巩割,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏付燥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一愈犹、第九天 我趴在偏房一處隱蔽的房頂上張望键科。 院中可真熱鬧,春花似錦漩怎、人聲如沸勋颖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽饭玲。三九已至,卻和暖如春叁执,著一層夾襖步出監(jiān)牢的瞬間茄厘,已是汗流浹背矮冬。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留次哈,地道東北人胎署。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像窑滞,于是被迫代替她去往敵國和親琼牧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容