wu
2023-11-30 0ece3ba8c92df0438af52b8de6b9225d8ada4103
springboot-vue3/src/main/java/com/example/springboot/security/config/ShiroConfig.java
New file
@@ -0,0 +1,185 @@
package com.example.springboot.security.config;
import com.example.springboot.security.JWTRealm;
import com.example.springboot.security.NoSessionFilter;
import com.example.springboot.security.StatelessDefaultSubjectFactory;
import com.example.springboot.security.UserRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import javax.servlet.Filter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
    /**
     * Shiro生命周期处理器
     *
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }
    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * DefaultAdvisorAutoProxyCreator的顺序必须在shiroFilterFactoryBean之前,不然SecurityUtils.getSubject().getPrincipal()获取不到参数
     *
     * @return
     */
    @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(defaultSecurityManager());
        return authorizationAttributeSourceAdvisor;
    }
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultSecurityManager());
        // 过滤规则
        Map<String, String> linkedHashMap = new LinkedHashMap<>();
        // 无状态登录情况下关闭了shiro中的session,导致所有需要加上authc接口请求时候都会报错,
        // 所以使用@RequiresRoles,@RequiresPermissions注解,aop方式实现接口的权限校验
        /* 添加shiro的内置过滤器,自定义url规则
         * Shiro自带拦截器配置规则
         * rest:比如/admins/user/**=rest[user],根据请求的方法,相当于/admins/user/**=perms[user:method] ,其中method为post,get,delete等
         * port:比如/admins/user/**=port[8081],当请求的url的端口不是8081是跳转到schemal://serverName:8081?queryString,其中schmal是协议http或https等,serverName是你访问的host,8081是url配置里port的端口,queryString是你访问的url里的?后面的参数
         * perms:比如/admins/user/**=perms[user:add:*],perms参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,比如/admins/user/**=perms["user:add:*,user:modify:*"],当有多个参数时必须每个参数都通过才通过,想当于isPermitedAll()方法
         * roles:比如/admins/user/**=roles[admin],参数可以写多个,多个时必须加上引号,并且参数之间用逗号分割,当有多个参数时,比如/admins/user/**=roles["admin,guest"],每个参数通过才算通过,相当于hasAllRoles()方法。//要实现or的效果看http://zgzty.blog.163.com/blog/static/83831226201302983358670/
         * anon:比如/admins/**=anon 没有参数,表示可以匿名使用
         * authc:比如/admins/user/**=authc表示需要认证才能使用,没有参数
         * authcBasic:比如/admins/user/**=authcBasic没有参数表示httpBasic认证
         * ssl:比如/admins/user/**=ssl没有参数,表示安全的url请求,协议为https
         * user:比如/admins/user/**=user没有参数表示必须存在用户,当登入操作时不做检查
         * 详情见文档 http://shiro.apache.org/web.html#urls-
         * */
        // 用户权限
//        linkedHashMap.put("/api/user/selectPage", "perms[user:select]");
//        linkedHashMap.put("/api/user/selectById", "perms[user:select]");
//        linkedHashMap.put("/api/user/updateById", "perms[user:update]");
//        linkedHashMap.put("/api/user/removeByIds", "perms[user:delete]");
//        // 商品权限
//        linkedHashMap.put("/api/product/deleteBatchIds", "perms[product:delete]");
//        linkedHashMap.put("/api/product/updateById", "perms[product:update]");
//        linkedHashMap.put("/api/product/insert", "perms[product:add]");
//
//        // 角色权限
//        linkedHashMap.put("/api/role/saveOrUpdate", "perms[role:add,role:update]");
//        linkedHashMap.put("/api/role/removeByIds", "perms[role:delete]");
//        linkedHashMap.put("/api/role/getById", "perms[role:select]");
//        linkedHashMap.put("/api/role/selectPage", "perms[role:select]");
//
//        // 菜单权限
//        linkedHashMap.put("/api/menuList/removeByIds", "perms[menuList:delete]");
//        linkedHashMap.put("/api/menuList/saveOrUpdate", "perms[menuList:add,menuList:update]");
//
//        // 订单权限
//        linkedHashMap.put("/api/order/deleteBatchIds", "perms[order:delete]");
//        linkedHashMap.put("/api/order/updateById", "perms[order:update]");
//
//        // 授权的权限
//        linkedHashMap.put("/api/rolePermission/saveOrUpdate", "perms[rolePermission:add]");
//        linkedHashMap.put("/api/rolePermission/removeByIds", "perms[rolePermission:delete]");
        // 自定义过滤器
        HashMap<String, Filter> filterHashMap = new HashMap<>();
        filterHashMap.put("jwt", new NoSessionFilter());
        shiroFilterFactoryBean.setFilters(filterHashMap);
        // 登录之后才可以请求的接口
        linkedHashMap.put("/api/**", "jwt");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(linkedHashMap);
        return shiroFilterFactoryBean;
    }
    @Bean
    public DefaultWebSecurityManager defaultSecurityManager() {
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealms(Arrays.asList(userRealm(), jwtRealm()));
        // 禁用shiro中的session
        DefaultSubjectDAO defaultSubjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        defaultSubjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        defaultWebSecurityManager.setSubjectDAO(defaultSubjectDAO);
        defaultWebSecurityManager.setSubjectFactory(subjectFactory());
        return defaultWebSecurityManager;
    }
    /**
     * 登录的认证和授权
     *
     * @return
     */
    @Bean
    public UserRealm userRealm() {
        UserRealm userRealm = new UserRealm();
        userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return userRealm;
    }
    /**
     * token的认证和授权
     *
     * @return
     */
    @Bean
    public JWTRealm jwtRealm() {
        return new JWTRealm();
    }
    @Bean
    public StatelessDefaultSubjectFactory subjectFactory() {
        return new StatelessDefaultSubjectFactory();
    }
    /*
     * 凭证匹配器 由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于MD5(MD5(""));
        return hashedCredentialsMatcher;
    }
    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setMaxAge(259200000);
        cookieRememberMeManager.setCookie(simpleCookie);
        cookieRememberMeManager.setCipherKey(Base64.decode("6ZmI6I2j5Y+R5aSn5ZOlAA=="));
        return cookieRememberMeManager;
    }
}