This article will introduce how to use Spring Security to control security in Spring Boot. The data of privilege control are queried by database.
1. background
Spring Security mainly adds filters before accessing. The main functions of the filters are accessing authentication manager and accessDecision Manager (which resources of the system can be accessed at that time, it involves querying database resources, and it also needs data resources to query security Metadata Source). The configuration address in the corresponding spring MVC is: http://blog.51cto.com/5148737/1615882, based on the above modifications, this paper will be transformed into spring boot version.
2. Adding maven dependencies
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- if necessary jsp Support security Label, you need to add this --> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-taglibs</artifactId> </dependency>
So far, the project path is as follows
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.gosun</groupId> <artifactId>cdn</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.4.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> </project>
HomeController.java
@Controller public class HomeController { @RequestMapping("/") public void index(HttpServletRequest request,HttpServletResponse response) throws IOException { response.getWriter().write("index"); } }
At this point, if the project runs, spring security will use internal default control, the console will print the password, user name, as follows
But what we're going to say is, custom permission control, ok, let's move on.
3. To build the security configuration file WebSecurity Config, you need to inherit the WebSecurity Configurer Adapter
In this class, user-defined access authentication, access decision maker and resource pool query are added.
@Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailService userDetailService; @Autowired private AuthenticationSuccessHandler authenticationSuccessHandler; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); authProvider.setPasswordEncoder(passwordEncoder()); authProvider.setUserDetailsService(userDetailService); ReflectionSaltSource saltSource = new ReflectionSaltSource(); saltSource.setUserPropertyToUse("username"); authProvider.setSaltSource(saltSource); auth.authenticationProvider(authProvider); } @Bean public Md5PasswordEncoder passwordEncoder() { return new Md5PasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable(); http.authorizeRequests() .antMatchers("/assets/**", "/portal/**", "/login","/redirect","/login/**").permitAll() .antMatchers("/schedual/area_isp_ip", "/403", "/404.jsp", "/logout", "/favicon.ico","/favicon.html").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .successHandler(authenticationSuccessHandler).defaultSuccessUrl("/").permitAll() .failureUrl("/login?error").permitAll() .and() .logout() .permitAll() .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { public <O extends FilterSecurityInterceptor> O postProcess( O fsi) { fsi.setSecurityMetadataSource(mySecurityMetadataSource()); fsi.setAccessDecisionManager(myAccessDecisionManager()); return fsi; } }); } @Bean public FilterInvocationSecurityMetadataSource mySecurityMetadataSource() { MyInvocationSecurityMetadataSourceService securityMetadataSource = new MyInvocationSecurityMetadataSourceService(); return securityMetadataSource; } @Bean public AccessDecisionManager myAccessDecisionManager() { return new MyAccessDecisionManager(); } }
Relevant notes:
-
This class needs to inherit the WebSecurity Configurer Adapter, override the configure method, and annotate
Configuration: Automatic loading for configuration files;
Enable Web Security: Opens security permission control
-
configure(HttpSecurity http)
http.csrf().disable() disables csrf, which is required to support jsp
http.authorizeRequests().xxxxx
Ant Matchers... for URLs that need to be filtered, that is, these address URLs do not require weight checking
formLogin().loginPage("/login") defines the default landing address
successHandler... defines the processing method after successful landing, mainly putting user information into session, etc.
failureUrl.... Defines a failed url
withObjectPostProcessor... defines two methods: access decision maker and resource pool query.
In spring mvc, filter is defined, which defines access decision maker, access decision maker and resource pool query in filter. In some online cases, when you see other articles spring boot integrating spring security, filter is added. But after experimentation, antMatchers will fail. If all URLs need permission checking, then you can use filter. Add the form of filter, but if you need some url s, such as static files, directly accessible to the above, you must use this way with ObjectPostProcessor.
-
Authentication Manager Builder auth defines access authentication
Among them, authProvider is the most important.
setPasswordEncoder: Define what password checking method is Md5PasswordEncoder
SetUser Details Service: Customized database query user
setSaltSource: Password Added Salt Value
4. Resource pool query class (MyInvocation Security Metadata Source Service)
public class MyInvocationSecurityMetadataSourceService implements FilterInvocationSecurityMetadataSource { private AntPathMatcher urlMatcher = new AntPathMatcher(); private static Map <String,Collection <ConfigAttribute>> resourceMap = null; @Autowired CommonDao commonDao; /** * Load URL permission configuration */ private void loadResourceDefine() { long starttime = new Date().getTime(); String sql = "SELECT * FROM auth_resource"; resourceMap = new HashMap<String,Collection <ConfigAttribute >> (); List<Properties> resourceList = commonDao.queryForList(sql); List<Map> roleList = commonDao.queryForList("SELECT * FROM auth_role"); Map<String,String> roleIdMap = new HashMap(); for(Map roleMap:roleList){ String roleId = roleMap.get("id").toString(); String rolename = roleMap.get("rolename").toString(); roleIdMap.put(roleId, rolename); } long endtime = new Date().getTime(); System.out.println("Complete query, time-consuming"+(endtime-starttime)+"ms"); for(Map dataMap:resourceList){ String urlPattern =""; if(dataMap.get("url_pattern")!=null){ urlPattern = dataMap.get("url_pattern").toString(); } String[] roleIds = new String[0]; if(dataMap.get("access_role")!=null&&!dataMap.get("access_role").equals("")){ String acce***ole = dataMap.get("access_role").toString(); roleIds = acce***ole.split(","); } Collection <ConfigAttribute> atts = new ArrayList < ConfigAttribute >(); for(String roleId:roleIds){ ConfigAttribute ca = new SecurityConfig(roleIdMap.get(roleId)); atts.add(ca); } resourceMap.put(urlPattern,atts); } endtime = new Date().getTime(); System.out.println("Loading system permission configuration is complete, time-consuming"+(endtime-starttime)+"ms"); } public boolean isResourceMapEmpty(){ if(resourceMap==null){ return true; }else{ return false; } } public Collection<ConfigAttribute> getAllConfigAttributes() { // TODO Auto-generated method stub return null; } public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException { if(resourceMap==null) { loadResourceDefine(); } // TODO Auto-generated method stub String url =((FilterInvocation)object).getRequestUrl(); if(resourceMap != null){ Set<String> urlPatternSet = resourceMap.keySet(); for(String urlPattern:urlPatternSet){ if(urlMatcher.match(urlPattern, url)){ return resourceMap.get(urlPattern); } } } return null; } public boolean supports(Class<?> arg0) { // TODO Auto-generated method stub return true; } public String resourceSelfMatcher(String resURL){ return null; } /** * Refresh resource allocation */ public void refreshResource(){ loadResourceDefine(); } }
5. Access Decision Manager
public class MyAccessDecisionManager implements AccessDecisionManager { @Autowired MyInvocationSecurityMetadataSourceService securityMetadataSource; public void decide(Authentication authentication, Object object,Collection<ConfigAttribute> configAttributes) throws AccessDeniedException,InsufficientAuthenticationException { // TODO Auto-generated method stub // if(securityMetadataSource.isResourceMapEmpty()){ // securityMetadataSource.refreshResource(); // } if (configAttributes == null ) throw new AccessDeniedException("Sorry, you don't have this permission."); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println(sdf.format(new Date())+":\t"+object.toString()); for(ConfigAttribute ca:configAttributes){ String needRole = ca.getAttribute(); for(GrantedAuthority userGA:authentication.getAuthorities()) { if(needRole.equals(userGA.getAuthority())) { // ga is user's role. return ; } } } throw new AccessDeniedException("Sorry, you don't have this permission."); } public boolean supports(ConfigAttribute arg0) { // TODO Auto-generated method stub return true; } public boolean supports(Class<?> arg0) { // TODO Auto-generated method stub return true; } }
6. MyUser Detail Service
@Service("myUserDetailService") public class MyUserDetailService implements UserDetailsService { @Autowired CommonDao commonDao; String userTable = "auth_user"; String roleTable = "auth_role"; String menuTable = "auth_resource"; public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException, DataAccessException { Map userMap= commonDao.queryForOne("SELECT * FROM `"+userTable+"` WHERE username='"+username+"'"); if(userMap!=null&userMap.containsKey("username")){ //Initialize role information Collection <GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(); String[] userRoles = userMap.get("user_role").toString().split(","); for(String userRole:userRoles){ Map roleMap = commonDao.queryForOne("SELECT * FROM `"+roleTable+"` WHERE id="+ userRole); if(roleMap!=null && roleMap.containsKey("rolename")){ SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roleMap.get("rolename").toString()); authorities.add(authority); } } boolean enabled = false; String nickname = username; String telephone = ""; String email = ""; int sex = 0; String password = ""; if(userMap.get("telephone")!=null) telephone = userMap.get("telephone").toString(); if(userMap.get("password")!=null) password = userMap.get("password").toString(); if(userMap.get("nickname")!=null) nickname = userMap.get("nickname").toString(); if(userMap.get("email")!=null) email = userMap.get("email").toString(); if(userMap.get("sex")!=null) sex = Integer.parseInt(userMap.get("sex").toString()); if(Integer.parseInt(userMap.get("enabled").toString())==1) enabled = true; User userdtails = new User(username, password, enabled, true, true,true, authorities,null,nickname,telephone,email,sex); return userdtails; }else{ return null; } } }
7. Authentication Success handler was successfully returned by permission checking
@Service public class AuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private CommonDao commonDao; private RequestCache requestCache; public AuthenticationSuccessHandler(){ this.requestCache = new HttpSessionRequestCache(); } @Override public void onAuthenticationSuccess(HttpServletRequest request,HttpServletResponse response, Authentication authentication)throws ServletException, IOException { User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal(); request.getSession().setAttribute("userDetails", userDetails); super.onAuthenticationSuccess(request, response, authentication); return; } }