• SpringSecurity动态配置权限


    一、数据库表添加数据

    二、代码

    package com.example.pojo;
    
    import java.io.Serializable;
    import java.util.List;
    
    public class Menu implements Serializable {
        private Integer id;//            `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
        private String url;//  `url` varchar(64) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '' COMMENT '请求路径规则',
        private String path;//            `path` varchar(64) DEFAULT NULL COMMENT '路由path',
        private String component;//            `component` varchar(64) DEFAULT NULL COMMENT '组件英文名称',
        private String name;//            `name` varchar(64) DEFAULT NULL COMMENT '组件中文名称',
        private String iconCls;//            `iconCls` varchar(64) DEFAULT NULL COMMENT '菜单图标',
        private Boolean keepAlive;//            `keepAlive` tinyint(1) NOT NULL DEFAULT '0' COMMENT '菜单切换时是否保活',
        private Boolean requireAuth;//            `requireAuth` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否登录后才能访问',
        private Integer pid;//            `pid` int(10) unsigned DEFAULT NULL COMMENT '父菜单Id',
        private Boolean enabled;//            `enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可用',
        private List<Role> roles;
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    
        public String getPath() {
            return path;
        }
    
        public void setPath(String path) {
            this.path = path;
        }
    
        public String getComponent() {
            return component;
        }
    
        public void setComponent(String component) {
            this.component = component;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getIconCls() {
            return iconCls;
        }
    
        public void setIconCls(String iconCls) {
            this.iconCls = iconCls;
        }
    
        public Boolean getKeepAlive() {
            return keepAlive;
        }
    
        public void setKeepAlive(Boolean keepAlive) {
            this.keepAlive = keepAlive;
        }
    
        public Boolean getRequireAuth() {
            return requireAuth;
        }
    
        public void setRequireAuth(Boolean requireAuth) {
            this.requireAuth = requireAuth;
        }
    
        public Integer getPid() {
            return pid;
        }
    
        public void setPid(Integer pid) {
            this.pid = pid;
        }
    
        public Boolean getEnabled() {
            return enabled;
        }
    
        public void setEnabled(Boolean enabled) {
            this.enabled = enabled;
        }
    
        public List<Role> getRoles() {
            return roles;
        }
    
        public void setRoles(List<Role> roles) {
            this.roles = roles;
        }
    }
    package com.example.mapper;
    
    import com.example.pojo.Menu;
    
    import java.util.List;
    
    public interface MenuMapper {
        List<Menu> getAllMenus();
    }
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.example.mapper.MenuMapper">
        <resultMap id="BaseResultMap" type="com.example.pojo.Menu">
            <id property="id" column="id"/>
            <result property="url" column="url"/>
            <collection property="roles" ofType="com.example.pojo.Role">
                <id property="id" column="rid"/>
                <result property="nameEn" column="rnameEn"/>
                <result property="nameCn" column="rnameCn"/>
            </collection>
        </resultMap>
    
        <select id="getAllMenus" resultMap="BaseResultMap">
            select m.*,r.id as rid,r.nameEn as rnameEn,r.nameCn as rnameCn
                from menu m
                    left join menu_role mr on m.id=mr.mid
                    left join role r on mr.rid=r.id
        </select>
    </mapper>
    package com.example.security;
    
    
    import com.example.mapper.MenuMapper;
    import com.example.pojo.Menu;
    import com.example.pojo.Role;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.access.SecurityConfig;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.web.FilterInvocation;
    import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
    import org.springframework.stereotype.Component;
    import org.springframework.util.AntPathMatcher;
    
    import javax.annotation.Resource;
    import java.util.Collection;
    import java.util.List;
    
    @Component
    public class DemoFilterInvocationSecurityMetadataSource
            implements FilterInvocationSecurityMetadataSource {
        private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfigurerAdapter.class);
        AntPathMatcher antPathMatcher = new AntPathMatcher();//用来实现ant风格的url匹配
    //    @Autowired
        @Resource
        MenuMapper menuMapper;
    
        /**
         *
         * @param object
         * @return Collection<ConfigAttribute>:请求当前url所需要的角色
         * @throws IllegalArgumentException
         */
        @Override
        public Collection<ConfigAttribute> getAttributes(Object object)
                throws IllegalArgumentException {
            String requestUrl = ((FilterInvocation) object).getRequestUrl();//提取当前请求的url
            List<Menu> allMenus = menuMapper.getAllMenus();//从数据库中获取Menu相关信息,也可以保存到缓存再从缓存(Redis)获取
            for (Menu menu : allMenus) {//遍历获取访问当前url所需要的所有角色,将它们返回
                if (antPathMatcher.match(menu.getUrl(), requestUrl)) {
                    List<Role> roles = menu.getRoles();
                    String[] roleArr = new String[roles.size()];
                    for (int i = 0; i < roleArr.length; i++) {
                        roleArr[i] = roles.get(i).getNameEn();
                    }
                    logger.info("=====DemoFilterInvocationSecurityMetadataSource=======roleArr==="+roleArr.toString());
                    return SecurityConfig.createList(roleArr);
                }
            }
            logger.info("=====DemoFilterInvocationSecurityMetadataSource=======ROLE_LOGIN===");
            return SecurityConfig.createList("ROLE_LOGIN");//请求的url在数据库表中不存在相应模式,就假设该请求登录即可访问,直接返回 ROLE_LOGIN
        }
        @Override
        public Collection<ConfigAttribute> getAllConfigAttributes() {
            return null;
        }
        @Override
        public boolean supports(Class<?> clazz) {
            return FilterInvocation.class.isAssignableFrom(clazz);
        }
    }
    package com.example.security;
    
    import org.springframework.security.access.AccessDecisionManager;
    import org.springframework.security.access.AccessDeniedException;
    import org.springframework.security.access.ConfigAttribute;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.GrantedAuthority;
    import org.springframework.stereotype.Component;
    
    import java.util.Collection;
    
    @Component
    public class DemoAccessDecisionManager
            implements AccessDecisionManager {
        //判断当前登录用户是否具有请示当前url所需要的角色,如不具备抛出异常,具备则不做任何操作
        @Override
        public void decide(Authentication auth,//auth:当前用户登录信息
                           Object object,//FilterInvocation 对象,可以获取当前请求的对象
                           Collection<ConfigAttribute> ca){//FilterInvocationSecurityMetadataSource中getAttributes()返回值,即当前url所需要的角色
            Collection<? extends GrantedAuthority> auths = auth.getAuthorities();
            for (ConfigAttribute configAttribute : ca) {
                if ("ROLE_LOGIN".equals(configAttribute.getAttribute())//如果需要的角色是ROLE_LOGIN,说明当前url是登录登录即可访问的url
                        && auth instanceof UsernamePasswordAuthenticationToken) {////如果auth是UsernamePasswordAuthenticationToken实例,说明当前用户已经登录
                    return;
                }
                for (GrantedAuthority authority : auths) {
                    if (configAttribute.getAttribute().equals(authority.getAuthority())) {
                        return;
                    }
                }
            }
            throw new AccessDeniedException("权限不足");
        }
    
        @Override
        public boolean supports(ConfigAttribute attribute) {
            return true;
        }
    
        @Override
        public boolean supports(Class<?> clazz) {
            return true;
        }
    }
    package com.example.security;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.ObjectPostProcessor;
    import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.builders.WebSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
    import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
    
    import javax.annotation.Resource;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Configuration
    @EnableWebSecurity
    public class DemoSecurityConfig extends WebSecurityConfigurerAdapter {
        private static final Logger logger = LoggerFactory.getLogger(WebSecurityConfigurerAdapter.class);
    
        @Autowired
        DemoUserDetailsService demoUserDetailsService;
    
        @Bean
        public PasswordEncoder passwordEncoder(){
    //        return NoOpPasswordEncoder.getInstance();
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        DemoFilterInvocationSecurityMetadataSource dfisms() {
            return new DemoFilterInvocationSecurityMetadataSource();
        }
        @Bean
        DemoAccessDecisionManager dadm() {
            return new DemoAccessDecisionManager();
        }
    
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //        auth.userDetailsService(userService);
            auth.userDetailsService(demoUserDetailsService);
        }
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            logger.info("==============configure==================================");
    //        super.configure(http);
            http.csrf().disable();
    //        http.authorizeRequests() //对请求进行授权
    ////                .antMatchers("/login").permitAll()
    ////                .antMatchers("/admin/**").hasRole("admin")
    ////                .antMatchers("/db/**").hasRole("dba")
    ////                .antMatchers("/user/**").hasRole("user")
    //                .anyRequest() //任何请求
    //                .authenticated()//都要进行身份认证
    //                .and()
    //                .formLogin()//表单登录
    //                .loginPage("/login")//登录页面
    //                .loginProcessingUrl("/login")
    //                .permitAll();//和登录相关的接口都不需要认证即可访问
    
            http.authorizeRequests()
                    .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                        @Override
                        public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                            object.setSecurityMetadataSource(dfisms());
                            object.setAccessDecisionManager(dadm());
                            return object;
                        }
                    })
                    .anyRequest() //任何请求
                    .authenticated()//都要进行身份认证
                    .and()
                    .formLogin()//表单登录
                    .loginPage("/login")//登录页面
    //                .loginProcessingUrl("/login")
                    .permitAll();//和登录相关的接口都不需要认证即可访问
    
    //        http.authorizeRequests().
    //                antMatchers("/static/**").permitAll().anyRequest().authenticated().
    //                and().formLogin().loginPage("/login").permitAll().successHandler(loginSuccessHandler()).
    //                and().logout().permitAll().invalidateHttpSession(true).
    //                deleteCookies("JSESSIONID").logoutSuccessHandler(logoutSuccessHandler()).
    //                and().sessionManagement().maximumSessions(10).expiredUrl("/login");
    
        }
    
        //======================================================================
    
        /**
         * 配置忽略的静态文件,不加的话,登录之前页面的css,js不能正常使用,得登录之后才能正常.
         */
        @Override
        public void configure(WebSecurity web) throws Exception {
            // 忽略URL
            web.ignoring().antMatchers("/**/*.js", "/lang/*.json", "/**/*.css", "/**/*.js", "/**/*.map", "/**/*.html",
                    "/**/*.png");
        }
    
        @Bean
        public LogoutSuccessHandler logoutSuccessHandler() { //登出处理
            return new LogoutSuccessHandler() {
                @Override
                public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
    
                }
    //            @Override
    //            public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
    //                try {
    //                    SecurityUser user = (SecurityUser) authentication.getPrincipal();
    ////                    logger.info("USER : " + user.getUsername() + " LOGOUT SUCCESS !  ");
    //                } catch (Exception e) {
    ////                    logger.info("LOGOUT EXCEPTION , e : " + e.getMessage());
    //                }
    //                httpServletResponse.sendRedirect("/login");
    //            }
            };
        }
    
        @Bean
        public SavedRequestAwareAuthenticationSuccessHandler loginSuccessHandler() { //登入处理
            return new SavedRequestAwareAuthenticationSuccessHandler() {
                @Override
                public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
                    User userDetails = (User) authentication.getPrincipal();
                    logger.info("USER : " + userDetails.getUsername() + " LOGIN SUCCESS !  ");
                    super.onAuthenticationSuccess(request, response, authentication);
                }
            };
        }
    
    //    @Bean
    //    UserDetailsService demoUserDetailsService() {
    //        return new DemoUserDetailsService();
    //    }
    
        @Bean
        public UserDetailsService userDetailsService() {    //用户登录实现
    
    //        return new DemoUserDetailsService();
            return new UserDetailsService() {
                //            @Override
    //            public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    //                return null;
    //            }
    //            @Autowired
    //            private UserRepository userRepository;
                @Resource
                private PasswordEncoder passwordEncoder;
    
                @Override
                public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                    logger.info("=-======loadUserByUsername======-=-========d================"+username);
    //                User user = userRepository.findByUsername(s);
    //                if (user == null) throw new UsernameNotFoundException("Username " + s + " not found");
    //                return new SecurityUser(user);
                    return new User(username,passwordEncoder.encode("123456"),
                            true,true,true,true,
                            AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
                }
            };
        }
    
    //    @Autowired
    //    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    //        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    //        auth.eraseCredentials(false);
    //    }
    }
  • 相关阅读:
    2018/12/21 HDU-2077 汉诺塔IV(递归)
    2018-12-08 acm日常 HDU
    2018/12/12 acm日常 第二周 第六题
    git 添加远程分支,并可以code review.
    zookeeper数据迁移方法
    gem install nokogiri -v '1.6.6.2' 出错
    gem install json -v '1.8.2' error
    gem install bundle 安装失败
    全能型开源远程终端:MobaXterm
    如何写好 Git Commit 信息
  • 原文地址:https://www.cnblogs.com/mingforyou/p/14617426.html
Copyright © 2020-2023  润新知