springboot supports spring security custom database queries

Keywords: Java Spring Maven xml

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:

  1. 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

  2. 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.


  3. 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;
    }
}



Posted by skeetley on Fri, 25 Jan 2019 13:36:13 -0800