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); // } }