SpringBoot SpringSecurity4整合

**

SpringBoot SpringSecurity4整合,灵活权限配置,弃用注解方式.

**
SpringSecurity 可以使用注解对方法进行细颗粒权限控制,但是很不灵活,必须在编码期间,就已经写死权限

其实关于SpringSecurity,大部分类都不需要重写,需要的只是妥善的配置.

每次修改权限以后,需要让MetaDataSource刷新 资源-权限 的MAP,这里应该需要做一些处理,或者优化.

这里实现,可以在后台随时开启关闭权限,不需要硬编码写死.而且资源的RequestMapping,可以是有多个地址

可以根据角色分配权限,也可以精确到为每一个用户分配权限,模块,或者方法.

这样比较灵活,但是UI会很复杂,用户也不好理解

SpringBoot SpringSecurity4整合_第1张图片资源注解:注解使用在控制器类,或者方法中.注解在类中,粗颗粒控制,注解在方法中细颗粒

/**
 * Created by ZhenWeiLai on on 2016-10-16.
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AclResc {
    int id();//ACLResource 因为特殊原因不使用 id 自动增长,所以必须自定义ID ,并且不能重复
    String code();
    String name();
    String homePage() default "";
    boolean isMenu() default true;
}
复制代码
注解在类:

@AclResc(id = 5000,code = "aclRescUser", name = AclRescUserController.MODULE_NAME,homePage = AclRescUserController.HOME_PAGE)
public class AclRescUserController extends BaseController 
注解在方法:

    @RequestMapping(value = "/list",method = RequestMethod.GET)
    @AclResc(id = 5001,code = "list",name = "用户资源列表")
    public ResultDataDto list(){

    }
系统完全启动后,更新资源信息:

复制代码
/**
 * Created by ZhenWeiLai on on 2016-10-16.
 * SpringBoot 启动完毕做些事情
 */
@Component
public class ApplicationStartup  implements CommandLineRunner {

    @Resource
    private AclResourceService aclResourceService;
    @Resource
    private AclAuthService aclAuthService;

    @Resource
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Resource
    private MySecurityMetadataSource securityMetadataSource;



    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void run(String... strings) throws Exception {
        /**
         * 初始化资源,保存到数据库
         */
        initModule();
        /**
         * Spring Security 需要的资源-权限
         */
        securityMetadataSource.doLoadResourceDefine();
    }


    /**
     * 读取所有Controller包括以内的方法
     */
    private void initModule() {
        /**
         * 模块 - 方法map
         */
        Map> resourcesMap = new HashMap<>();
        Map map = requestMappingHandlerMapping.getHandlerMethods();
        for (RequestMappingInfo info : map.keySet()) {
            AclResc moduleAclResc = map.get(info).getBeanType().getAnnotation(AclResc.class);
            if (moduleAclResc != null) {
                if (StringUtils.isBlank(moduleAclResc.homePage()))
                    throw new RuntimeException("使用:" + AclResc.class.getName() + " 注解类时,请配置 homePage ");
                Class aclResourceClass = map.get(info).getBeanType();
                RequestMapping moduleMapping = aclResourceClass.getAnnotation(RequestMapping.class);
                AclResource moduleResc = new AclResource(moduleAclResc.id(), moduleAclResc.code(), moduleAclResc.name(), Arrays.toString(moduleMapping.value()), AclResource.Type.MODULE.getCode(), moduleAclResc.homePage(), moduleAclResc.isMenu());
                if (moduleMapping != null) {
                    List resources;
                    AclResource methodResc;
                    Method method = map.get(info).getMethod();
                    AclResc methodAclResc = method.getAnnotation(AclResc.class);
                    if (methodAclResc != null) {
                        methodResc = new AclResource(methodAclResc.id(), methodAclResc.code(), methodAclResc.name(), info.getPatternsCondition().toString().replace("||", Delimiter.COMMA.getDelimiter()), AclResource.Type.METHOD.getCode(), null);
                        if (resourcesMap.get(moduleResc) == null) {
                            resources = new ArrayList<>();
                            resources.add(methodResc);
                            resourcesMap.put(moduleResc, resources);
                        } else {
                            resourcesMap.get(moduleResc).add(methodResc);
                        }
                    }
                }
            }
        }
        addModule(resourcesMap);
    }

    /**
     * 检查新模块,添加到数据库,并更新视图的模块ID
     *
     * @param resourcesMap
     */
    private void addModule(Map> resourcesMap) {
        for (Map.Entry> item : resourcesMap.entrySet()) {
            AclResource resultResc = aclResourceService.findEntityById(item.getKey().getId());
            //如果模块是新模块,那么新增到数据库
            if (resultResc == null) {
                aclResourceService.addEntity(item.getKey());
                List resources = item.getValue();
                for (AclResource resc : resources) {
                    resc.setModuleId(item.getKey().getId());
                }
            } else {
                //如果已存在模块,那么更新需要的字段
                aclResourceService.updateEntity(item.getKey());
                List resources = item.getValue();
                for (AclResource methodResc : resources) {
                    //方法模块CODE 根据 模块CODE + 方法CODE 生成
                    methodResc.setCode(item.getKey().getCode() + "_" + methodResc.getCode());
                    methodResc.setModuleId(resultResc.getId());
                    AclResource oringinalMethodResc = aclResourceService.findEntityById(methodResc.getId());
                    if (oringinalMethodResc != null) {
                        //RequestMapping可能被修改,所以这里要做一次更新
                        aclResourceService.updateEntity(methodResc);
                        //同时code也可能被更改,所以更新权限code
                        aclAuthService.updateCodeByRescId(methodResc.getCode(), methodResc.getId());
                    } else {
                        aclResourceService.addEntity(methodResc);
                    }
                }
            }
        }
    }

}

构建权限菜单:


/**
     * 根据用户权限构建菜单
     */
    @Override
    public Map> getAclUserMenus() {

        //创建完整的菜单,然后删除没有权限的菜单
        Map> userMenuModuleMap = findAclMenuModuleMap();
        //获取资源/权限集
        Map> moduleMap = securityMetadataSource.getModuleMap();
        for (String path : moduleMap.keySet()) {
            //如果没有权限
            if (!SecurityUtil.hastAnyAuth(moduleMap.get(path))) {
                Iterator userMenuModuleMapKey = userMenuModuleMap.keySet().iterator();
                while (userMenuModuleMapKey.hasNext()) {
                    AclMenu key = userMenuModuleMapKey.next();
                    List modules = userMenuModuleMap.get(key);
                    if (modules.isEmpty()) {
                        userMenuModuleMapKey.remove();
                        continue;
                    }
                    Iterator aclResourceIterator = modules.iterator();
                    while (aclResourceIterator.hasNext()) {
                        String rescPath = aclResourceIterator.next().getPath();
                        String[] pathArr = rescPath.substring(1, rescPath.length() - 1).split(Delimiter.COMMA.getDelimiter());
                        for (String item : pathArr) {
                            if (item.equals(path)) {
                                //从菜单模块中删除
                                aclResourceIterator.remove();
                                //如果模块为空
                                if (modules.isEmpty()) {
                                    //删除菜单
                                    userMenuModuleMapKey.remove();
                                }
                            }
                        }

                    }
                }
            }
        }
        return userMenuModuleMap;
    }

FilterInvocationSecurityMetadataSource:

/**
 * Created by ZhenWeiLai on 2016-10-16.
 */
@Component("securityMetadataSource")
public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

    private static Map> moduleMap = null;

    private static Map> methodMap = null;

    @Resource
    private AclResourceService aclResourceService;

    @Resource
    private AclRescRoleService aclRescRoleService;

    @Resource
    private AclRoleService aclRoleService;

    @Resource
    private AclAuthService aclAuthService;

    @Override
    public Collection getAttributes(Object object) throws IllegalArgumentException {
        Collection collection;
        collection = getAttributesHandler(methodMap, object);
        if (collection != null)
            return collection;
        collection = getAttributesHandler(moduleMap, object);
        return collection;
    }

    /**
     * 处理方法
     *
     * @param map
     * @return
     */
    private Collection getAttributesHandler(Map> map, Object object) {
        HttpServletRequest request = ((FilterInvocation) object).getRequest();
        Iterator var3 = map.entrySet().iterator();
        Map.Entry entry;
        do {
            if (!var3.hasNext()) {
                return null;
            }
            entry = (Map.Entry) var3.next();

        } while (!(new AntPathRequestMatcher(entry.getKey().toString())).matches(request));
        return (Collection) entry.getValue();
    }


    //4
    @Override
    public Collection getAllConfigAttributes() {
        Set allAttributes = new HashSet();
        Map> all = new HashMap<>(this.moduleMap);
        all.putAll(this.methodMap);
        Iterator var2 = all.entrySet().iterator();
        while (var2.hasNext()) {
            Map.Entry> entry = (Map.Entry) var2.next();
            allAttributes.addAll(entry.getValue());
        }

        return allAttributes;
    }

    //3
    @Override
    public boolean supports(Class clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }


    @Transactional(readOnly = true)
    private void loadResourceDefine() {
        loadModuleResources();
        loadMethodResources();
    }


    /**
     * 提供一个外部使用方法.获取module权限MAP;
     *
     * @return
     */
    public Map> getModuleMap() {
        Map> map = new HashMap<>(moduleMap);
        return map;
    }


    /**
     * 提供外部方法让Spring环境启动完成后调用
     */
    public void doLoadResourceDefine() {
        loadResourceDefine();
    }


    /**
     * 读取模块资源
     */
    private void loadModuleResources() {
        /**
         * 查询模块资源权限,配置模块权限验证
         */
        List aclResources = aclResourceService.findAllModule();

        //模块资源为KEY,角色为Value 的list
        moduleMap = new HashMap<>();
        for (AclResource module : aclResources) {
            /**
             * 加载所有模块资源
             */
            List aclRescRoles = aclRescRoleService.findByRescId(module.getId());


            /**
             * 无论如何超级管理员拥有所有权限
             */
            stuff(new SecurityConfig(SecurityUtil.ADMIN), moduleMap, module.getPath());

            for (AclRescRole aclRescRole : aclRescRoles) {
                Integer roleId = aclRescRole.getRoleId();//角色ID
                String roleCode = aclRoleService.findEntityById(roleId).getCode();//角色编码
                stuff(new SecurityConfig(roleCode.toUpperCase()), moduleMap, module.getPath());
            }
        }
    }


    /**
     * 读取精确方法权限资源
     */
    private void loadMethodResources() {
        /**
         * 因为只有权限控制的资源才需要被拦截验证,所以只加载有权限控制的资源
         */
        //方法资源为key,权限编码为
        methodMap = new HashMap<>();
        List> pathAuths = aclAuthService.findPathCode();
        for (Map pathAuth : pathAuths) {
            String path = pathAuth.get("path").toString();
            ConfigAttribute ca = new SecurityConfig(pathAuth.get("code").toString().toUpperCase());
            stuff(ca, methodMap, path);
        }
    }

    private void stuff(ConfigAttribute ca, Map> map, String path) {

        String[] pathArr = path.substring(1, path.length() - 1).split(Delimiter.COMMA.getDelimiter());
        for (String item : pathArr) {
            Collection collection = map.get(item + "/**");
            if (collection != null) {
                collection.add(ca);
            } else {
                collection = new ArrayList<>();
                collection.add(ca);
                String pattern = StringUtils.trimToEmpty(item) + "/**";
                map.put(pattern, collection);
            }
        }
    }
}

最后:

/**
 * Created by ZhenWeiLai on on 2016-10-16.
 * 

* 三种方法级权限控制 *

* 1.securedEnabled: Spring Security’s native annotation * 2.jsr250Enabled: standards-based and allow simple role-based constraints * 3.prePostEnabled: expression-based */ @EnableWebSecurity //@EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsService userDetailsService; @Resource private MySecurityMetadataSource securityMetadataSource; @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/css/**"); web.ignoring().antMatchers("/images/**"); web.ignoring().antMatchers("/js/**"); //忽略登录界面 web.ignoring().antMatchers("/login"); //注册地址不拦截 // web.ignoring().antMatchers("/reg"); } @Override protected void configure(HttpSecurity http) throws Exception { //解决不允许显示在iframe的问题 http.headers().frameOptions().disable(); http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); http.authorizeRequests().anyRequest().fullyAuthenticated(); //自定义过滤器 MyFilterSecurityInterceptor filterSecurityInterceptor = new MyFilterSecurityInterceptor(securityMetadataSource,accessDecisionManager(),authenticationManagerBean()); //在适当的地方加入 http.addFilterAt(filterSecurityInterceptor,FilterSecurityInterceptor.class); http.exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login")).and().logout().logoutUrl("/logout").logoutSuccessUrl("/login").and().exceptionHandling().accessDeniedPage("/accessDenied"); // 关闭csrf http.csrf().disable(); //session管理 //session失效后跳转 http.sessionManagement().invalidSessionUrl("/login"); //只允许一个用户登录,如果同一个账户两次登录,那么第一个账户将被踢下线,跳转到登录页面 http.sessionManagement().maximumSessions(1).expiredUrl("/login"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 自定义UserDetailsService,设置加密算法 auth.userDetailsService(userDetailsService); //.passwordEncoder(passwordEncoder()) //不删除凭据,以便记住用户 auth.eraseCredentials(false); } UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter() throws Exception { UsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter(); usernamePasswordAuthenticationFilter.setPostOnly(true); usernamePasswordAuthenticationFilter.setAuthenticationManager(this.authenticationManager()); usernamePasswordAuthenticationFilter.setUsernameParameter("name_key"); usernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key"); usernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/checkLogin", "POST")); usernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler()); usernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler()); return usernamePasswordAuthenticationFilter; } // @Bean // public LoggerListener loggerListener() { // System.out.println("org.springframework.security.authentication.event.LoggerListener"); // return new LoggerListener(); // } // // @Bean // public org.springframework.security.access.event.LoggerListener eventLoggerListener() { // System.out.println("org.springframework.security.access.event.LoggerListener"); // return new org.springframework.security.access.event.LoggerListener(); // } /** * 投票器 */ private AbstractAccessDecisionManager accessDecisionManager() { List> decisionVoters = new ArrayList(); decisionVoters.add(new AuthenticatedVoter()); decisionVoters.add(new RoleVoter());//角色投票器,默认前缀为ROLE_ RoleVoter AuthVoter = new RoleVoter(); AuthVoter.setRolePrefix("AUTH_");//特殊权限投票器,修改前缀为AUTH_ decisionVoters.add(AuthVoter); AbstractAccessDecisionManager accessDecisionManager = new AffirmativeBased(decisionVoters); return accessDecisionManager; } @Override public AuthenticationManager authenticationManagerBean() { AuthenticationManager authenticationManager = null; try { authenticationManager = super.authenticationManagerBean(); } catch (Exception e) { e.printStackTrace(); } return authenticationManager; } /** * 验证异常处理器 * * @return */ private SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() { return new SimpleUrlAuthenticationFailureHandler("/getLoginError"); } // /** // * 表达式控制器 // * // * @return // */ // private DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() { // DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); // return webSecurityExpressionHandler; // } // /** // * 表达式投票器 // * // * @return // */ // private WebExpressionVoter webExpressionVoter() { // WebExpressionVoter webExpressionVoter = new WebExpressionVoter(); // webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler()); // return webExpressionVoter; // } // Code5 官方推荐加密算法 // @Bean("passwordEncoder") // public BCryptPasswordEncoder passwordEncoder() { // BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); // return bCryptPasswordEncoder; // } // // Code3---------------------------------------------- /** * 登录成功后跳转 * 如果需要根据不同的角色做不同的跳转处理,那么继承AuthenticationSuccessHandler重写方法 * * @return */ private SimpleUrlAuthenticationSuccessHandler authenticationSuccessHandler() { return new SimpleUrlAuthenticationSuccessHandler("/loginSuccess"); } /** * Created by ZhenWeiLai on on 2016-10-16. */ public static class MyFilterSecurityInterceptor extends FilterSecurityInterceptor { public MyFilterSecurityInterceptor(FilterInvocationSecurityMetadataSource securityMetadataSource, AccessDecisionManager accessDecisionManager, AuthenticationManager authenticationManager){ this.setSecurityMetadataSource(securityMetadataSource); this.setAccessDecisionManager(accessDecisionManager); this.setAuthenticationManager(authenticationManager); } } }

你可能感兴趣的:(SpringBoot SpringSecurity4整合)