项目GitHub地址 :
https://github.com/FrameReserve/TrainingBoot
Spring Boot (三)集成spring security,标记地址:
https://github.com/FrameReserve/TrainingBoot/releases/tag/0.0.3
pom.xml
只列举 Spring Security配置,完整配置请查看Git项目地址
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <!-- 依赖版本 --> <mybatis.version>3.4.1</mybatis.version> <mybatis.spring.version>1.3.0</mybatis.spring.version> <spring-security.version>4.1.0.RELEASE</spring-security.version> </properties> <!-- spring security --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-web</artifactId> <version>${spring-security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-config</artifactId> <version>${spring-security.version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> <version>${spring-security.version}</version> </dependency>
Spring Security 配置类:
src/main/java/com/training/core/security/WebSecurityConfig.java
package com.training.core.security; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.vote.AuthenticatedVoter; import org.springframework.security.access.vote.RoleVoter; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.event.LoggerListener; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.AccessDeniedHandlerImpl; import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; import org.springframework.security.web.access.expression.WebExpressionVoter; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import com.training.sysmanager.service.AclResourcesService; import com.training.sysmanager.service.impl.AclResourcesServiceImpl; /** * Created by Athos on 2016-10-16. */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Resource private UserDetailsService userDetailsService; @Resource private MySecurityMetadataSource mySecurityMetadataSource; @Override protected void configure(HttpSecurity http) throws Exception { http.addFilterAfter(MyUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); // 开启默认登录页面 http.authorizeRequests().anyRequest().authenticated().withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { public <O extends FilterSecurityInterceptor> O postProcess(O fsi) { fsi.setSecurityMetadataSource(mySecurityMetadataSource); fsi.setAccessDecisionManager(accessDecisionManager()); fsi.setAuthenticationManager(authenticationManagerBean()); return fsi; } }).and().exceptionHandling().authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/login.html")).and().logout().logoutSuccessUrl("/index.html").permitAll(); // 自定义accessDecisionManager访问控制器,并开启表达式语言 http.exceptionHandling().accessDeniedHandler(accessDeniedHandler()).and().authorizeRequests().anyRequest().authenticated().expressionHandler(webSecurityExpressionHandler()); // 自定义登录页面 http.csrf().disable(); // 自定义注销 // http.logout().logoutUrl("/logout").logoutSuccessUrl("/login") // .invalidateHttpSession(true); // session管理 http.sessionManagement().maximumSessions(1); // RemeberMe // http.rememberMe().key("webmvc#FD637E6D9C0F1A5A67082AF56CE32485"); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 自定义UserDetailsService auth.userDetailsService(userDetailsService); } @Bean UsernamePasswordAuthenticationFilter MyUsernamePasswordAuthenticationFilter() { UsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter = new UsernamePasswordAuthenticationFilter(); myUsernamePasswordAuthenticationFilter.setPostOnly(true); myUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManagerBean()); myUsernamePasswordAuthenticationFilter.setUsernameParameter("name_key"); myUsernamePasswordAuthenticationFilter.setPasswordParameter("pwd_key"); myUsernamePasswordAuthenticationFilter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/login", "POST")); myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler()); return myUsernamePasswordAuthenticationFilter; } @Bean AccessDeniedHandler accessDeniedHandler() { AccessDeniedHandlerImpl accessDeniedHandler = new AccessDeniedHandlerImpl(); accessDeniedHandler.setErrorPage("/securityException/accessDenied"); return accessDeniedHandler; } @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(); } /* * * 这里可以增加自定义的投票器 */ @Bean(name = "accessDecisionManager") public AccessDecisionManager accessDecisionManager() { List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList(); decisionVoters.add(new RoleVoter()); decisionVoters.add(new AuthenticatedVoter()); decisionVoters.add(webExpressionVoter());// 启用表达式投票器 MyAccessDecisionManager accessDecisionManager = new MyAccessDecisionManager(decisionVoters); return accessDecisionManager; } @Bean(name = "authenticationManager") @Override public AuthenticationManager authenticationManagerBean() { AuthenticationManager authenticationManager = null; try { authenticationManager = super.authenticationManagerBean(); } catch (Exception e) { e.printStackTrace(); } return authenticationManager; } @Bean(name = "failureHandler") public SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler() { return new SimpleUrlAuthenticationFailureHandler("/getLoginError"); } @Bean(name = "aclResourcesService") @ConditionalOnMissingBean public AclResourcesService aclResourcesService() { return new AclResourcesServiceImpl(); } /* * 表达式控制器 */ @Bean(name = "expressionHandler") public DefaultWebSecurityExpressionHandler webSecurityExpressionHandler() { DefaultWebSecurityExpressionHandler webSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); return webSecurityExpressionHandler; } /* * 表达式投票器 */ @Bean(name = "expressionVoter") public WebExpressionVoter webExpressionVoter() { WebExpressionVoter webExpressionVoter = new WebExpressionVoter(); webExpressionVoter.setExpressionHandler(webSecurityExpressionHandler()); return webExpressionVoter; } }
自定义 UserDetailsService 用户、角色、资源获取类:
src/main/java/com/training/core/security/UserDetailsServiceImpl.java
package com.training.core.security; import java.util.ArrayList; import java.util.List; import javax.annotation.Resource; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; 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.stereotype.Service; import com.training.sysmanager.entity.AclResources; import com.training.sysmanager.entity.AclUser; import com.training.sysmanager.service.AclResourcesService; import com.training.sysmanager.service.AclRoleResourcesService; import com.training.sysmanager.service.AclUserService; /** * Created by Athos on 2016-10-16. */ @Service("userDetailsService") public class UserDetailsServiceImpl implements UserDetailsService { @Resource private AclUserService aclUserService; @Resource private AclRoleResourcesService aclRoleResourcesService; @Resource private AclResourcesService aclResourcesService; /* (non-Javadoc) * @see org.springframework.security.core.userdetails.UserDetailsService#loadUserByUsername(java.lang.String) */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>(); AclUser aclUser = aclUserService.findAclUserByName(username); String resourceIds = aclRoleResourcesService.selectResourceIdsByRoleIds(aclUser.getRoleIds()); List<AclResources> aclResourcesList = aclResourcesService.selectAclResourcesByResourceIds(resourceIds); for (AclResources aclResources : aclResourcesList) { auths.add(new SimpleGrantedAuthority(aclResources.getAuthority().toUpperCase())); } // auths.addAll(aclResourcesList.stream().map(resources -> new SimpleGrantedAuthority(resources.getAuthority().toUpperCase())).collect(Collectors.toList())); return new User(aclUser.getUserName().toLowerCase(),aclUser.getUserPwd().toLowerCase(),true,true,true,true,auths); } }
自定义securityMetadataSource:
src/main/java/com/training/core/security/MySecurityMetadataSource.java
package com.training.core.security; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.SecurityConfig; import org.springframework.security.web.FilterInvocation; import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.stereotype.Component; import com.training.sysmanager.entity.AclResources; import com.training.sysmanager.service.AclResourcesService; /** * Created by Athos on 2016-10-16. */ @Component("mySecurityMetadataSource") public class MySecurityMetadataSource implements FilterInvocationSecurityMetadataSource { private static Map<String,Collection<ConfigAttribute>> aclResourceMap = null; private AclResourcesService aclResourcesService; /** * 构造方法 */ //1 public MySecurityMetadataSource(AclResourcesService aclResourcesService){ this.aclResourcesService=aclResourcesService; loadResourceDefine(); } @Override public Collection<ConfigAttribute> getAttributes(Object object)throws IllegalArgumentException{ HttpServletRequest request=((FilterInvocation)object).getRequest(); Iterator<String> ite = aclResourceMap.keySet().iterator(); while (ite.hasNext()){ String resURL = ite.next(); RequestMatcher requestMatcher = new AntPathRequestMatcher(resURL); if(requestMatcher.matches(request)){ return aclResourceMap.get(resURL); } } return null; } //4 @Override public Collection<ConfigAttribute> getAllConfigAttributes() { System.out.println("metadata : getAllConfigAttributes"); return null; } //3 @Override public boolean supports(Class<?> clazz) { System.out.println("metadata : supports"); return true; } private void loadResourceDefine(){ /** * 因为只有权限控制的资源才需要被拦截验证,所以只加载有权限控制的资源 */ List<AclResources> aclResourceses = aclResourcesService.selectAclResourcesTypeOfRequest(); aclResourceMap = new HashMap<>(); for (AclResources aclResources:aclResourceses){ ConfigAttribute ca = new SecurityConfig(aclResources.getAuthority().toUpperCase()); String url = aclResources.getUrl(); if(aclResourceMap.containsKey(url)){ Collection<ConfigAttribute> value = aclResourceMap.get(url); value.add(ca); aclResourceMap.put(url,value); }else { Collection<ConfigAttribute> atts = new ArrayList<ConfigAttribute>(); atts.add(ca); aclResourceMap.put(url,atts); } } } }
自定义AbstractAccessDecisionManager权限决策类,
src/main/java/com/training/core/security/MyAccessDecisionManager.java
package com.training.core.security; import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.access.vote.AbstractAccessDecisionManager; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import java.util.Collection; import java.util.Iterator; import java.util.List; /** * Created by Athos on 2016-10-16. */ public class MyAccessDecisionManager extends AbstractAccessDecisionManager { protected MyAccessDecisionManager(List<AccessDecisionVoter<? extends Object>> decisionVoters) { super(decisionVoters); } @Override public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { if(configAttributes==null){ return; } Iterator<ConfigAttribute> ite = configAttributes.iterator(); while(ite.hasNext()){ ConfigAttribute ca = ite.next(); String needRole = (ca).getAttribute(); for (GrantedAuthority ga : authentication.getAuthorities()){ if (needRole.equals(ga.getAuthority())){ return; } } } throw new AccessDeniedException("没有权限,拒绝访问!"); } @Override public boolean supports(ConfigAttribute attribute) { return false; } /** * Iterates through all <code>AccessDecisionVoter</code>s and ensures each can support * the presented class. * <p> * If one or more voters cannot support the presented class, <code>false</code> is * returned. * * @param clazz the type of secured object being presented * @return true if this type is supported */ @Override public boolean supports(Class<?> clazz) { return true; } }
用户、角色、资源(菜单)略。
详情请看GIT完成工程。
https://github.com/FrameReserve/TrainingBoot