spring boot 使用 shiro+redis管理權限


spring boot中比較簡單的權限管理選擇了使用shiro

然后用shiro-redis管理session,如下:

創建個shiroConfing,里面設置ShiroFilterFactoryBean------SecurityManager------myShiroRealm

然后在securityManager中設置緩存和session管理的方式如定義一個sessionManager指定用redis來操作session

然后開啟shiro AOP注解支持。

創建自己的realm繼承AuthorizingRealm做登陸跟權限的認證過程。

代碼如下

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shirFile(SecurityManager securityManager) {
        log.info("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //攔截器
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        //自定義攔截器
        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        filters.put("authc", new MyFormAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filters);

        // 配置不會被攔截的鏈接 順序判斷
        filterChainDefinitionMap.put("/static/**", "anon");//配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了;
        //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問-->
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登錄成功后要跳轉的鏈接
        shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授權界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    @Bean
    public MyShiroRealm myShiroRealm(){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return myShiroRealm;
    }

    /**
     * 憑證匹配器
     * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher(){
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:這里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(2);//散列的次數,比如散列兩次,相當於 md5(md5(""));
        return hashedCredentialsMatcher;
    }

    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myShiroRealm());
        // 自定義session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        // 自定義緩存實現 使用redis
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }

    //自定義sessionManager
    @Bean
    public SessionManager sessionManager() {
        SessionConfig mySessionManager = new SessionConfig();
        mySessionManager.setSessionDAO(redisSessionDAO());
        return mySessionManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis開源插件
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host+":"+port);
        // redisManager.setExpire(18000);// 配置緩存過期時間
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * cacheManager 緩存 redis實現
     * 使用的是shiro-redis開源插件
     */
    @Bean
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setPrincipalIdFieldName("uid");
        return redisCacheManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao層的實現 通過redis
     * <p>
     * 使用的是shiro-redis開源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

    /**
     *  開啟shiro aop注解支持.
     *  使用代理方式;所以需要開啟代碼支持;
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean(name="simpleMappingExceptionResolver")
    public SimpleMappingExceptionResolver
    createSimpleMappingExceptionResolver() {
        SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
        Properties mappings = new Properties();
        mappings.setProperty("DatabaseException", "databaseError");//數據庫異常處理
        mappings.setProperty("UnauthorizedException","403");
        r.setExceptionMappings(mappings);  // None by default
        r.setDefaultErrorView("error");    // No default
        r.setExceptionAttribute("ex");     // Default is "exception"
        return r;
    }
}

 

public class MyShiroRealm extends AuthorizingRealm {/**
     * 登陸認證的實現*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//通過username從數據庫中查找 User對象,如果找到,沒找到.
        //實際項目中,這里可以根據實際情況做緩存,如果不做,Shiro自己也是有時間間隔機制,2分鍾內不會重復執行該方法
        UserInfo queryInfo = new UserInfo();
        queryInfo.setUsername((String) token.getPrincipal());
     //--------------------------------------------------------
List<SysRole> roleList = new ArrayList<>(); for (UserInfo info : userInfos) { List<SysRole> tmpRoleList = info.getRoleList(); for (SysRole sysRole : tmpRoleList) { sysRole.setPermissions(sysPermissionMapper.selectPermissionByRoleId(sysRole.getId())); roleList.add(sysRole); } } userInfo.setRoleList(roleList); SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( userInfo,//用戶名 userInfo.getPassword(),//密碼 ByteSource.Util.bytes(userInfo.getCredentialsSalt()),//這里是密碼+鹽 getName()//realm name ); return authenticationInfo; } /** * 權限驗證 */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal(); for (SysRole sysRole : userInfo.getRoleList()) { authorizationInfo.addRole(sysRole.getRole()); for (SysPermission permission : sysRole.getPermissions()) { authorizationInfo.addStringPermission(permission.getPermission()); } } return authorizationInfo; } }

登陸Controller

@Controller
public class LoginController {
@RequestMapping({"/", "/index"}) public String index() { System.out.println("goIndex"); return "user/index"; } @RequestMapping("/403") public String unauthorizedRole() { System.out.println("------沒有權限-------"); return "403"; } @GetMapping("/login") public String goLoginPage(Model m) { System.out.print("goLogin"); Subject currentUser = SecurityUtils.getSubject(); if(!currentUser.isAuthenticated()){ return "login"; }else{ return "user/index"; } } @PostMapping("/login") public String login(@ModelAttribute UserInfo userinfo, HttpServletRequest request, Map<String, Object> map) {// 登錄失敗從request中獲取shiro處理的異常信息。 // shiroLoginFailure:就是shiro異常類的全類名. Subject currentUser = SecurityUtils.getSubject();// 通過Subject對象的isAuthenticated判斷該用戶是否已經驗證 if (!currentUser.isAuthenticated()) { // 如果該用戶未驗證,得到用戶的賬號和密碼,使用UsernamePasswordToken的方式生成一個該用戶的token憑證 UsernamePasswordToken token = new UsernamePasswordToken( userinfo.getUsername(), userinfo.getPassword()); // 開啟記住我的功能,這里可以通過獲取用戶的提交的信息,判斷是否選擇記住我來決定開啟或關閉 token.setRememberMe(true); String exception = (String) request.getAttribute("shiroLoginFailure"); System.out.println("exception=" + exception); String msg = ""; try { // 通過Subject對象的login來驗證用戶的身份 currentUser.login(token); log.info("login in sessionid:"+currentUser.getSession().getId().toString()); // 如果用戶身份驗證不通過會拋出相應的異常,可以通過拋出的異常來設置返回給前台的信息 } catch (UnknownAccountException uae) { log.info("===============賬號不存在異常=>" + token.getPrincipal()); msg = "UnknownAccountException -- > 賬號不存在:"; } catch (IncorrectCredentialsException ice) { log.info("===============密碼錯誤異常=>" + token.getPrincipal()); msg = "IncorrectCredentialsException -- > 密碼不正確:"; } catch (LockedAccountException lae) { log.info("===============賬戶被鎖定異常=>" + token.getPrincipal()); msg = "kaptchaValidateFailed -- > 驗證碼錯誤"; } catch (AuthenticationException ae) { log.info("===============即驗證不通過異常,為前面幾個異常的父類=>" + token.getPrincipal()); msg = "else >> " + exception; } map.put("msg", msg); } // 此方法不處理登錄成功,由shiro進行處理 return "login"; } }
使登陸成功的用戶跳轉倒index頁
/**
 * 使登陸成功的用戶跳轉倒index
 */
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
    @Override
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
        if (!StringUtils.isEmpty(getSuccessUrl())) {
            // getSession(false):如果當前session為null,則返回null,而不是創建一個新的session
            Session session = subject.getSession(false);
            if (session != null) {
                session.removeAttribute("shiroSavedRequest");
            }
        }
        return super.onLoginSuccess(token, subject, request, response);
//     或者下面這種方法
//        String successUrl="/index";
//        WebUtils.issueRedirect(request,response,successUrl);
//        System.out.println("登錄首頁攔截");
//        return false
    }
}

在Controller中用注解進行接口權限控制

@RequiresPermissions("student:add")

使用攔截器進行權限不足時返回的信息處理

@ControllerAdvice
public class ExceptionHandleController  {
    static final Logger log = LoggerFactory.getLogger(ExceptionHandleController.class);

    @ExceptionHandler(UnauthorizedException.class)
    public void handleShiroException(Exception ex,HttpServletRequest request, HttpServletResponse response) {
        log.info(ex.toString());
        WebUtils.sendJson(response,false,"沒有權限!"); //該方法返回json 如 {"success":true, "msg":"沒有權限"}
//        return "redirect:/error/403";
    }

    @ExceptionHandler(AuthorizationException.class)
    public void AuthorizationException(Exception ex) {
        log.info(ex.toString());
//        return "redirect:/error/401";
    }
}

 


免責聲明!

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



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