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);
//    }
}
原文地址:https://www.cnblogs.com/mingforyou/p/14617426.html