springboot整合shiro

1. pom.xml文件引入依賴(lài)

<properties>
    <shiro.version>1.3.2</shiro.version>
</properties>

<!-- 集成shiro -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>${shiro.version}</version>
</dependency>
<!-- shiro緩存 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
    <version>${shiro.version}</version>
</dependency>

2. 創(chuàng)建shiro配置類(lèi)

import com.qfedu.rongzaiboot.shiro.UserRealm;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * shiro配置
 */
@Configuration
public class ShiroConfig {

    /**
     * session管理器
     * @return
     */
    @Bean(name = "sessionManager")
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        //設(shè)置session的過(guò)期時(shí)間為1小時(shí),(默認(rèn)時(shí)間時(shí)30分鐘)
        sessionManager.setGlobalSessionTimeout(60*60*1000);
        //開(kāi)啟掃描session線(xiàn)程朗兵,清理超時(shí)會(huì)話(huà)
        sessionManager.setSessionValidationSchedulerEnabled(true);
        //禁用了url重寫(xiě) 去掉URL中的JSESSIONID
        sessionManager.setSessionIdUrlRewritingEnabled(false);//默認(rèn)true
        return sessionManager;
    }


    /**
     * 創(chuàng)建SecurityManager
     */
    @Bean
    public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager){
        //密碼加密規(guī)則
        /*HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        credentialsMatcher.setHashAlgorithmName("md5");
        credentialsMatcher.setHashIterations(1024);
        //credentialsMatcher.setHashSalted(true);
        userRealm.setCredentialsMatcher(credentialsMatcher);*/

        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm);
        securityManager.setSessionManager(sessionManager);
        securityManager.setCacheManager(ehCacheManager());
        return securityManager;
    }

    /**
     * 創(chuàng)建shiroFilter過(guò)濾器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
        //anon:它對(duì)應(yīng)的過(guò)濾器里面是空的,什么都沒(méi)做,這里.do和.jsp后面的*表示參數(shù),比方說(shuō)login.jsp?main -->
        //authc:該過(guò)濾器下的頁(yè)面必須驗(yàn)證后才能訪(fǎng)問(wèn),它是Shiro內(nèi)置的一個(gè)攔截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/login.html");//沒(méi)有認(rèn)證時(shí)跳轉(zhuǎn)到的登陸頁(yè)
        shiroFilter.setSuccessUrl("/index.html");//認(rèn)證成功跳轉(zhuǎn)到主頁(yè)
        shiroFilter.setUnauthorizedUrl("/unauthorized.json");//未授權(quán)時(shí)的跳轉(zhuǎn)鏈接

        Map<String,String> filterMap = new LinkedHashMap<>();
        filterMap.put("/public/**","anon"); //放行靜態(tài)資源的路徑
        filterMap.put("/login.html","anon");
        filterMap.put("/sys/login","anon");
        filterMap.put("/captcha.jpg","anon");//驗(yàn)證碼的圖片
        //filterMap.put("/**","authc");//authc經(jīng)過(guò)認(rèn)證才能訪(fǎng)問(wèn)

        //角色驗(yàn)證 具有admin角色的用戶(hù)可以訪(fǎng)問(wèn)
        //filterMap.put("/sys/menu/del","roles[admin]");
        //權(quán)限驗(yàn)證 具有perms[sys:menu:update]可以訪(fǎng)問(wèn)
        //filterMap.put("/sys/menu/update","perms[sys:menu:update]");

        filterMap.put("/**","user");//通過(guò)記住我訪(fǎng)問(wèn)

        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

    /**
     * 創(chuàng)建shiro緩存
     * @return
     */
    @Bean
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
        return ehCacheManager;
    }

    /**
     * ShiroConfig配置文件中開(kāi)啟注解
     * 配置三個(gè)bean:
     * lifecycleBeanPostProcessor
     * defaultAdvisorAutoProxyCreator
     * authorizationAttributeSourceAdvisor
     */

    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

}

2.1 創(chuàng)建自定義Realm類(lèi) 實(shí)現(xiàn)認(rèn)證和授權(quán)

package com.qfedu.rongzaiboot.shiro;

import com.qfedu.rongzaiboot.entity.SysUser;
import com.qfedu.rongzaiboot.service.SysUserService;
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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Set;

@Component
public class UserRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 認(rèn)證
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        System.out.println("認(rèn)證........");

        String usernameInput = (String) token.getPrincipal();
        String passwordInput = new String((char[])token.getCredentials());
        //查詢(xún)用戶(hù)是否存在
        SysUser user = sysUserService.queryByUserName(usernameInput);
        if(user == null){
            throw new UnknownAccountException("賬號(hào)或密碼不正確");
        }

        //數(shù)據(jù)庫(kù)中獲取的用戶(hù)名和密碼
        String username = user.getUsername();
        String password = user.getPassword();

        //判斷密碼是否正確
        if(!passwordInput.equals(user.getPassword())){
            throw new IncorrectCredentialsException("賬號(hào)或密碼不正確");
        }

        //判斷用戶(hù)賬號(hào)是否被鎖定
        if (user.getStatus() == 0){
            throw new LockedAccountException("賬號(hào)已被鎖定污淋,請(qǐng)聯(lián)系管理員");
        }

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, this.getName());

        return info;
    }

    /**
     * 授權(quán)
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        System.out.println("授權(quán)........");

        SysUser user = (SysUser) principals.getPrimaryPrincipal();
        Set<String> userPermissions = sysUserService.getUserPermissions(user.getUserId());

        SimpleAuthorizationInfo authorInfo = new SimpleAuthorizationInfo();
        authorInfo.addStringPermissions(userPermissions);

        //角色授權(quán)
        /*List<String> roleList = Arrays.asList("admin");
        authorInfo.addRoles(roleList);*/

        //資源授權(quán)
        /*List<String> permList = Arrays.asList("sys:menu:update");
        authorInfo.addStringPermissions(permList);*/

        return authorInfo;
    }
}

3. 創(chuàng)建service和dao

3.1 SysUserService接口和SysUserServiceImpl實(shí)現(xiàn)類(lèi)

//接口
public interface SysUserService {

    /**
     * 根據(jù)用戶(hù)名,查詢(xún)系統(tǒng)用戶(hù)
     */
    SysUser queryByUserName(String username);
}

//實(shí)現(xiàn)類(lèi)
@Service
public class SysUserServiceImpl implements SysUserService {

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Override
    public SysUser queryByUserName(String username) {
        return sysUserMapper.queryByUserName(username);
    }

    @Override
    public Set<String> getUserPermissions(Long userId) {
        List<String> permsList = null;

        //超級(jí)管理員
        if (userId == 1) {
            List<SysMenu> menuList = sysMenuMapper.queryListAll();
            permsList = new ArrayList<>(menuList.size());
            for (SysMenu menu : menuList) {
                permsList.add(menu.getPerms());
            }
        }else {
            //普通用戶(hù)授權(quán)
            permsList = sysUserMapper.queryAllPerms(userId);
        }

        Set<String> permsSet = new HashSet<>();
        for (String perms : permsList) {
            if (StringUtils.isBlank(perms)) {
                continue;
            }
            permsSet.addAll(Arrays.asList(perms.trim().split(",")));
        }
        return permsSet;
    }
}

3.2 mapper的sql語(yǔ)句

  <select id="queryByUserName" resultType="com.qfedu.rongzaiboot.entity.SysUser">
    select * from sys_user where username = #{username}
  </select>

    <select id="queryAllPerms" resultType="java.lang.String">
      SELECT perms FROM sys_menu m
        LEFT JOIN sys_role_menu rm ON m.menu_id = rm.menu_id
        LEFT JOIN sys_user_role ur ON ur.role_id = rm.role_id
        WHERE ur.user_id = #{userId}
    </select>

  <select id="queryListAll" resultType="com.qfedu.rongzaiboot.entity.SysMenu">
    select menu_id,name,url,perms,type,icon,order_num,(select p.name from sys_menu p where p.menu_id = m.parent_id) as parentName
    from sys_menu m order by m.order_num asc
  </select>

4. LoginController中添加登錄controller方法

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.qfedu.rongzaiboot.utils.R;
import com.qfedu.rongzaiboot.utils.ShiroUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Map;

@Controller
public class SysLoginController {

    @Autowired
    private Producer producer;

    /**
     * 生成驗(yàn)證碼
     * @param response
     * @throws IOException
     */
    @RequestMapping("/captcha.jpg")
    public void kaptcha(HttpServletResponse response) throws IOException {
        //避免瀏覽器緩存
        response.setHeader("Cache-Control", "no-store,no-cache");
        response.setContentType("image/jpeg");

        //生成文字驗(yàn)證碼
        String text = producer.createText();

        //生成圖片驗(yàn)證碼
        BufferedImage image = producer.createImage(text);

        //保存在session中
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);

        //響應(yīng)給客戶(hù)端
        ServletOutputStream outputStream = response.getOutputStream();
        ImageIO.write(image, "jpg", outputStream);
        outputStream.flush();//清空緩沖區(qū)
    }

    /**
     * 登陸方法
     * @param map
     * @return
     */
    @ResponseBody
    @RequestMapping("/sys/login")
    public R login(@RequestBody Map<String,String> map) {
        String username = map.get("username");
        String password = map.get("password");
        String captcha = map.get("captcha");
        String rememberMe = map.get("rememberMe");

        String sessionCaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
        if (!captcha.equalsIgnoreCase(sessionCaptcha)) {
            return R.error("驗(yàn)證碼不正確");
        }

        boolean remember = false;
        if (rememberMe != null){
            remember = true;
        }

        Subject subject = ShiroUtils.getSubject();

        try {
            password = new Md5Hash(password,username,1024).toHex();
            UsernamePasswordToken token = new UsernamePasswordToken(username, password);

            //設(shè)置記住我
            token.setRememberMe(remember);

            subject.login(token);
        }catch (UnknownAccountException e){
            return R.error(e.getMessage());
        }catch (IncorrectCredentialsException e){
            return R.error(e.getMessage());
        }catch (LockedAccountException e){
            return R.error(e.getMessage());
        }catch (AuthenticationException e){
            return R.error("賬戶(hù)驗(yàn)證失敗");
        }

        return R.ok();
    }

    /**
     * 退出方法
     * @return
     */
    @GetMapping("/logout")
    public String logout(){
        ShiroUtils.logout();
        return "redirect:login.html";
    }

}

5. shiro緩存的配置 文件ehcache.xml

<ehcache>
    <diskStore path="java.io.tmpdir"/>
    <defaultCache

            maxEntriesLocalHeap="2000"
            maxElementsInMemory="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            overflowToDisk="true" />
</ehcache>

Ehcache配置文件的詳細(xì)說(shuō)明
[http://blog.csdn.net/mlitsn/article/details/1909192](http://blog.csdn.net/mlitsn/article/details/1909192)

 緩存存活時(shí)間和失效時(shí)間:
[http://www.cnblogs.com/sprinkle/p/6539086.html](http://www.cnblogs.com/sprinkle/p/6539086.html) 

設(shè)置緩存的大小
[http://elim.iteye.com/blog/2116749](http://elim.iteye.com/blog/2116749)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末余掖,一起剝皮案震驚了整個(gè)濱河市寸爆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌盐欺,老刑警劉巖赁豆,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異找田,居然都是意外死亡歌憨,警方通過(guò)查閱死者的電腦和手機(jī)着憨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)墩衙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事漆改⌒牧澹” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵挫剑,是天一觀的道長(zhǎng)去扣。 經(jīng)常有香客問(wèn)我,道長(zhǎng)樊破,這世上最難降的妖魔是什么愉棱? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮哲戚,結(jié)果婚禮上奔滑,老公的妹妹穿的比我還像新娘。我一直安慰自己顺少,他們只是感情好朋其,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著脆炎,像睡著了一般梅猿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秒裕,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天袱蚓,我揣著相機(jī)與錄音,去河邊找鬼几蜻。 笑死癞松,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的入蛆。 我是一名探鬼主播响蓉,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼哨毁!你這毒婦竟也來(lái)了枫甲?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤扼褪,失蹤者是張志新(化名)和其女友劉穎想幻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體话浇,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脏毯,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幔崖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片食店。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡渣淤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吉嫩,到底是詐尸還是另有隱情价认,我是刑警寧澤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布自娩,位于F島的核電站用踩,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏忙迁。R本人自食惡果不足惜脐彩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望姊扔。 院中可真熱鬧丁屎,春花似錦、人聲如沸旱眯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)删豺。三九已至共虑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間呀页,已是汗流浹背妈拌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蓬蝶,地道東北人尘分。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丸氛,于是被迫代替她去往敵國(guó)和親培愁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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

  • 一缓窜、什么是shiro Apache Shiro是一個(gè)全面的定续、蘊(yùn)含豐富功能的安全框架。下圖為描述Shiro功能的框架...
    夏日橘子冰閱讀 411評(píng)論 0 1
  • 一禾锤、前言 我們?cè)趯W(xué)習(xí)springboot的時(shí)候說(shuō)過(guò),要完成某項(xiàng)功能,引入一些依賴(lài),我們第一反應(yīng)就是這個(gè)功能看看有沒(méi)...
    藍(lán)雄威閱讀 1,763評(píng)論 0 11
  • 強(qiáng)烈推薦老炮兒私股,不會(huì)失望的,看得很痛快恩掷。 最喜歡的一場(chǎng)戲是最后六爺騎自行車(chē)去約架的路上倡鲸,遇到那只從籠子里逃出來(lái)的鴕...
    筑牙閱讀 504評(píng)論 2 5
  • 午夜, 帶著孤獨(dú)者的失憶黄娘, 像瘋了的蔓藤峭状, 自由伸展克滴,無(wú)法入睡, 失眠……
    墨凡M閱讀 104評(píng)論 0 1
  • 1.我想擁有的:媽媽不再插手我的生活氮凝。 2.行為:如果媽媽不在插手我的生活羔巢,我就不會(huì)那么焦慮,可以心平氣和地和媽媽...
    疏桐_sw閱讀 299評(píng)論 0 3