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

猜你喜欢

转载自blog.csdn.net/qq_36472439/article/details/85048645