package com.gihon.configSecurity.config; import com.gihon.configSecurity.encoder.MyPasswordEncoder; import com.gihon.configSecurity.handler.MyAccessDeniedHandler; import com.gihon.configSecurity.handler.MyFailureHandler; import com.gihon.configSecurity.handler.MySuccessHandler; 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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl; import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import javax.sql.DataSource; /** * SpringSecurity的配置类型 * 继承父类型WebSecurityConfigurerAdapter * WebSecurityConfigurerAdapter是一个Security提供的配置模板,可以快速实现 * Security配置,简化自定义配置的复杂度。 * * SpringSecurity提供的默认环境,如:登录页面、登录处理请求(/login)、拦截处理(未登录访问其他地址自动跳转到登录页面) * 只有在不提供自定义Security配置的时候,才有效。 * * Security要求自定义的UserDetailsService配置配套提供一个PasswordEncoder类型的对象, * 对象中提供密码的加密和匹配逻辑,为认证提供加密处理。 * 如果spring环境中没有PasswordEncoder类型的对象,UserDetailsService类型的对象无效。 */ @Configuration public class MySecurityConfiguration extends WebSecurityConfigurerAdapter { /** * 在Security中,处理认证逻辑的具体代码,不需要提供控制器逻辑。 * 因为Security根据配置formLogin().loginProcessingUrl()自动做控制跳转。 * 跳转逻辑是: * 找UserDetailsService接口的实现类型对象,调用启动的方法loadUserByUsername,实现登录认证。 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { // 设置登录页面路径地址是 '/' // http.formLogin() // 获取Security中专门用来配置登录相关逻辑的配置类型 // .usernameParameter("name") // 设置请求参数中,用户名的参数名称 // .passwordParameter("pswd") // 设置请求参数中,密码的参数名称 // .loginPage("/") // 配置登录页面请求地址是什么。 //.loginProcessingUrl("/login") // 使用POST访问,访问什么地址的时候,实现认证逻辑。登录地址。 // 认证成功或失败,Security会转发请求,到指定的地址。转发的请求是POST方式的。需要定义控制器处理。 // .successForwardUrl("/success") // 登录成功后,跳转地址。 请求转发, 重复登录。 // .defaultSuccessUrl("/success") // 登录成功后,重定向到指定位置 // .successHandler(new MySuccessHandler("/security/success")) // 登录成功后,自定义处理逻辑。 // .failureForwardUrl("/fail"); // 登录失败后,跳转地址 // .failureUrl("/fail"); // 登录失败后,响应重定向 // .failureHandler(new MyFailureHandler("/security/fail")); // 登录失败后,自定义处理逻辑 /** * 设置权限相关 * 可以通过对ExpressionUrlAuthorizationConfigurer配置,实现对URL地址的权限管理。 * 可以定义针对固定地址、表达式地址的权限控制。 * 常用权限控制方法: * 1、 permitAll() - 全权限访问,任意客户端都可访问。 * 2、 authenticated() - 必须认证后才能访问。登录后才能访问。 * 3、 denyAll() - 无访问权限。 没有登录的时候,要求登录;登录后,显示403无访问权限错误。 * 4、 anonymous() - 匿名访问。可以不登录就访问的资源。和permitAll类似。 * 5、 rememberMe() - 记住我。当认证的时候,开启了rememberMe功能,可以使用记住我访问。 * 6、 fullyAuthenticated() - 完整认证才可访问。 rememberMe不能访问。 * * 联合使用: * 如: antMatchers("/", "/index").permitAll(); 请求 "/" 或 "/index" 的时候,授予所有权限。 * 基于角色和权限的访问控制: * 必须建立在已认证的基础上,才会执行对应的访问控制。 * 1、 基于角色的访问控制: * URL匹配().hasRole | hasAnyRole * hasRole(String role) - 判断已登录的用户权限集合中,是否包含参数角色 * 底层是调用的access("hasRole('ROLE_"+role+"')") * hasAnyRole(String... roles) - 判断已登录的用户权限集合中,是否包括参数中的任意角色。 * 底层是调用的access("hasAnyRole('ROLE_角色1','ROLE_角色2'....)") * 2、 基于权限的访问控制: * hasAuthority(String authority) - 判断已登录的用户权限集合中,是否包含权限 * 底层: access("hasAuthority('权限')") * hasAnyAuthority(String... authorities) - 判断已登录的用户权限集合中,是否包含参数中任意权限 * 底层: access("hasAnyAuthority('权限1','权限2'....)") * 3、 基于IP的访问控制: * hasIpAddress(String ipAddress) 检查客户端IP是否是参数指定的IP * 客户端IP是: HttpServletRequest.getRemoteAddr()方法的返回值。 * 底层: access("hasIpAddress('IP地址')") */ http.authorizeRequests() // 获取Security中专门配置权限管理的配置类型 .antMatchers("/login").permitAll() .antMatchers("/doc.html","/webjars/**","/img.icons/**","/swagger-resources/**","/v2/api-docs").permitAll() // .antMatchers("/", "/fail").permitAll() // 访问 '/' 地址的时候,不需要登录。 // .antMatchers("/**/*.js").permitAll() // 只处理js文件 // .regexMatchers(".+[.]css").permitAll() // 只处理css // .antMatchers("/anonymous").anonymous() // 匿名访问 // .antMatchers("/denyAll").denyAll() // 不能访问 // .antMatchers("/hasAdminRole").hasRole("管理员") // 判断有没有"管理员"角色 // .antMatchers("/hasUserRole").hasRole("普通用户") // 判断有没有"普通用户"角色 // .antMatchers("/hasAnyRole").hasAnyRole("管理员", "普通用户") // 判断是否包含管理员或普通用户角色。 // .antMatchers("/hasAuthority").hasAuthority("userManagement") // .antMatchers("/hasAnyAuthority").hasAnyAuthority("userManagement","selfManagement") // .antMatchers("/accessRole").access("hasRole('ROLE_管理员')")// 使用access,角色控制 // .antMatchers("/accessAuthority").access("hasAuthority('selfManagement')")// 使用access,权限控制 // WebSecurityExpressionRoot //.antMatchers("/*").access("@myPermissionsMatcherImpl.hasPermission(request,authentication)") .anyRequest().authenticated(); // 所有的请求地址,都必须登录才能访问。 // 异常处理配置 http.exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler()); // 配置rememberMe, 使用rememberMe功能的时候,认证成功结果跳转,一定要使用重定向。 http.rememberMe() .rememberMeParameter("rememberMe") // 设置请求参数名,默认为remember-me .rememberMeCookieName("rememberMe") // 设置记住我的cookie名称。默认为remember-me .tokenValiditySeconds(300) // 设置rememberMe有效时长。默认14天。 .userDetailsService(userLoginServiceImpl) // 设置用户认证逻辑对象 .tokenRepository(tokenRepository); // 设置rememberMe数据持久化对象。持久化用户认证成功后的主体。 // 退出配置, 注销配置 http.logout() .invalidateHttpSession(true) // 退出的时候,销毁HttpSession对象。默认true .clearAuthentication(true) // 退出的时候,清空已认证用户主体对象。默认true .logoutUrl("/security/logout") // 退出登录的请求地址。默认是 /logout .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "GET")) .logoutSuccessUrl("/security/"); // 退出后的跳转地址,重定向。 默认是 loginPage?logout // csrf安全处理, 关闭csrf。 防止恶意攻击的技术。 security4+ 默认开启的。 http.csrf().disable(); } // @Autowired // private DataSource dataSource; // rememberMe数据保存对象,相当于是DAO @Bean public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){ // 使用Spring-JDBC技术,实现rememberMe数据的存储。保存到数据库。 // 在JdbcTokenRepositoryImpl类型的对象中,必须提供一个DataSource对象。 JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl(); jdbcTokenRepository.setDataSource(dataSource); // rememberMe表格的创建, 只能在系统第一次启动的时候创建。 默认是false,不会启动创建表格。 // jdbcTokenRepository.setCreateTableOnStartup(true); return jdbcTokenRepository; } @Autowired private UserDetailsService userLoginServiceImpl; @Autowired private PersistentTokenRepository tokenRepository; // 传递的构造参数BCryptPasswordEncoder(int strength), 建议为8的整数倍。 @Bean public PasswordEncoder passwordEncoder(){ return new MyPasswordEncoder(); } }