1. 程式人生 > >Shiro認證-初學(二)

Shiro認證-初學(二)

1:前後端分離:更改以前的ajax請求,由於目前的專案基本上都是前後端分離的,所以我們所有的資料都已JSON格式返回給前端,對沒有登入的請求進行攔截,覆蓋掉shiro原本的跳轉到login.jsp頁面,繼承FormAuthenticationFilter

public class AjaxPermissionsAuthorizationFilter extends FormAuthenticationFilter {
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("returnCode", ErrorEnum.E_20011.getErrorCode());
        jsonObject.put("returnMsg", ErrorEnum.E_20011.getErrorMsg());
        PrintWriter out = null;
        HttpServletResponse res = (HttpServletResponse) response;
        try {
            res.setCharacterEncoding("UTF-8");
            res.setContentType("application/json");
            out = response.getWriter();
            out.println(jsonObject);
        } catch (Exception e) {
        } finally {
            if (null != out) {
                out.flush();
                out.close();
            }
        }
        return false;
    }

    @Bean
    public FilterRegistrationBean registration(AjaxPermissionsAuthorizationFilter filter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(filter);
        registration.setEnabled(false);
        return registration;
    }
}

2:自定義Realm,重寫兩個介面方法,doGetAuthenticationInfo(登入)和doGetAuthorizationInfo(授權)

登入:拿資料匹配,查詢資料庫通過,建立SimpleAuthenticationInfo物件傳入使用者名稱和密碼,呼叫SecurityUtils.getSubject().getSession.setAttribute()存session

授權:呼叫SecurityUtils.getSubject.getSession()獲取session,從session中獲取到使用者的許可權,構建SimpleAuthorizationInfo物件,授權方法addStringPermissions()方法進行授權

public class UserRealm extends AuthorizingRealm {
    Logger logger= LoggerFactory.getLogger("UserRealm");
    @Autowired
    private LoginService loginService;
    /**
     * 授權
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        Session session=SecurityUtils.getSubject().getSession();
        JSONObject perssions= (JSONObject) session.getAttribute(Constants.SESSION_USER_PERMISSION);
        logger.info("使用者的permissions:"+perssions);
        logger.info("使用者的許可權為:"+perssions.get("permissionList"));
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermissions((Collection<String>) perssions.get("perssionList"));
        return simpleAuthorizationInfo;
    }

    /**
     * 登入
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String loginName= (String) authenticationToken.getPrincipal();
        String password= (String) authenticationToken.getCredentials();
        JSONObject user=loginService.getUser(loginName,password);
        if(user==null){
            //使用者不存在
            throw new UnknownAccountException();
        }
        //使用者存在
        SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(
                user.getString("username"),
                user.getString("password"),
                getName());
        user.remove("password");
        //將使用者資訊放入session中
        SecurityUtils.getSubject().getSession().setAttribute(Constants.SESSION_USER_INFO,user);
        return authenticationInfo;
    }
}

Shiro配置類:自定義shiro的過濾器鏈 Map結構

anon:他對應過濾器鏈裡面是空的,不用做什麼操作

authc:必須授權認證後的接口才可以訪問,他是Shiro內建的一個攔截器FormAuthenFilter

@Configuration
public class ShiroConfiguration {
    private Logger logger= LoggerFactory.getLogger("ShiroConfiguration");
    /**
     * 全場最重要的一步shiro的web過濾器Factory
     * 自定義Shiro過濾鏈,Map結構
     * Map中key(xml中是指value值)的第一個'/'代表的路徑是相對於HttpServletRequest.getContextPath()的值來的
     * anon:它對應的過濾器裡面是空的,什麼都沒做,這裡.do和.jsp後面的*表示引數,比方說login.jsp?main這種
     * authc:該過濾器下的頁面必須驗證後才能訪問,它是Shiro內建的一個攔截器org.apache.shiro.web.filter.authc.FormAuthenticationFilter
     */
    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        //shiro的安全介面
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String,Filter> filterMap=new LinkedHashMap<>();
        //此處為自定義ajax
        filterMap.put("authc", new AjaxPermissionsAuthorizationFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
        /**
         *過濾鏈定義,從上向下順序執行,一般將 / ** 放在最為下邊:這是一個坑呢,一不小心程式碼就不好使了;
         *authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問
         */
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login/auth", "anon");
        filterChainDefinitionMap.put("/login/logout", "anon");
        filterChainDefinitionMap.put("/error", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
    //第一步
    /**
     * 不指定名字的話,自動建立一個方法名第一個字母小寫的bean
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(userRealm());
        return securityManager;
    }

    /**
     * Shiro Realm 繼承自AuthorizingRealm的自定義Realm,即指定Shiro驗證使用者登入的類為自定義的
     */
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        return userRealm;
    }

    /**
     * 第四部:憑證匹配器
     */
    @Bean(name = "credentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //雜湊演算法:這裡使用MD5演算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //雜湊的次數,比如雜湊兩次,相當於 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        //storedCredentialsHexEncoded預設是true,此時用的是密碼加密用的是Hex編碼;false時用Base64編碼
        hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
        return hashedCredentialsMatcher;
    }
    /**
     * 第三步,管理shiro的生命週期
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**第二步:開啟Shiro註解(如@RequiresRoles,@RequiresPerssions,
     * 藉助SpringAOP掃描使用Shiro註解的類
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

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

原始碼地址:https://gitee.com/zhoujin_LoveCoding/shiro-demo