1、什么是Spring Security
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为基于Spring框架,所以Spring Security充分利用了依赖注入(dependency injection,DI)和面向切面的技术。
Spring security借助一系列Servlet Filter来提供各种安全性功能。DelegatingFilterProxy是一个特殊的Servlet Filter,它会将工作委托给一个javax.servlet.Filter实现类,这个实现类作为一个<bean>注册在Spring应用的上下文。如图所示:
在XML中可以使用<filter>元素配置:
<filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter>
在这里,最重要的是<filter-name>设置成了springSecurityFilterChain。这是因为我们马上就会将Spring Security配置在Web安全性之中,这里会有一个名为springSecurityFilterChain的Filter bean,DelegatingFilterProxy会将过滤逻辑委托给它。 如果你希望借助WebApplicationInitializer以Java的方式来配置DelegatingFilterProxy的话,那么我们所需要做的就是创建一个扩展的新类:
package spittr.config; import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer; public class SecurityWebInitializer extends AbstractSecurityWebApplicationInitializer { }
AbstractSecurityWebApplicationInitializer实现了WebApplicationInitializer,因此Spring会发现它,并用它在Web容器中注册DelegatingFilterProxy。尽管我们可以重载它的appendFilters()或insertFilters()方法来注册自己选择的Filter,但是要注册DelegatingFilterProxy的话,我们并不需要重载任何方法。 不管我们通过web.xml还是通过AbstractSecurityWebApplicationInitializer的子类来配置DelegatingFilterProxy,它都会拦截发往应用中的请求,并将请求委托给ID为springSecurityFilterChain bean。 springSecurityFilterChain本身是另一个特殊的Filter,它也被称为FilterChainProxy。它可以链接任意一个或多个其他的Filter。Spring Security依赖一系列Servlet Filter来提供不同的安全特性。但是,你几乎不需要知道这些细节,因为你不需要显式声明springSecurityFilterChain以及它所链接在一起的其他Filter。当我们启用Web安全性的时候,会自动创建这些Filter。
2、Spring Security在SpringBoot实战中的应用
引用依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
在启动类上加上如下注解:
/** * security.basic.enabled=false 被废弃了,现在使用 * @SpringBootApplication(exclude= SecurityAutoConfiguration.class) 代替 */ @EnableAsync @EnableDiscoveryClient @ComponentScan(basePackages = "com.bloc.core.*") @SpringBootApplication(exclude= SecurityAutoConfiguration.class) public class BlocCoreApplication { public static void main(String[] args) { SpringApplication.run(BlocCoreApplication.class, args); } }
创建WebAuthenticationEntryPoint组件:
@Component public class WebAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { // 因为用的是restful 没有登陆页面 在用户未登录的情况下返回一个401 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"); } }
创建一个过滤器SecuritySessionFilter组件:
@Component public class SecuritySessionFilter extends OncePerRequestFilter { private static Logger logger = LoggerFactory.getLogger(SecuritySessionFilter.class); @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { HttpSession session = request.getSession(false); if (session != null) { logger.info("SecuritySessionFilter session loading..."); AuthUserDetails authUser = (AuthUserDetails) session.getAttribute(Contants.USER); if (authUser != null && SecurityContextHolder.getContext().getAuthentication() == null) { logger.info("SecuritySessionFilter Authentication info:{}",authUser.getAuthorities()); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(authUser, null, authUser.getAuthorities()); authentication.setDetails((new WebAuthenticationDetailsSource()).buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } filterChain.doFilter(request, response); } }
添加WebSecurityConfig配置:
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired UserDetailsService userDetailsService; @Autowired WebAuthenticationEntryPoint webAuthenticationEntryPoint; @Autowired SecuritySessionFilter securitySessionFilter; private HttpSecurity http; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } @Bean @Override protected AuthenticationManager authenticationManager() throws Exception { return super.authenticationManager(); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .exceptionHandling().authenticationEntryPoint(this.webAuthenticationEntryPoint) .and() .authorizeRequests() // 登录放行 .antMatchers("/login").permitAll() //所有请求都拦截 .anyRequest().authenticated() .and().csrf().disable(); http.addFilterBefore(securitySessionFilter, UsernamePasswordAuthenticationFilter.class); } }
重写UserDetailsService接口:
@Service public class AuthUserDetailsServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; // 查询数据库中角色配置 @Autowired private RoleService roleService; // 查询用户的信息 @Autowired private UserService userService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userService.findUserByName(username); List<Role> roleByUid = roleService.findRoleByUid(user.getId()); StringBuffer buffer = new StringBuffer(); for (Role r: roleByUid) { buffer.append(r.getRoleName()).append(","); } buffer.deleteCharAt(buffer.length()-1); String encode = passwordEncoder.encode(user.getPassWord()); // AuthUserDetails 继承了User类,重写了里面的方法,返回我们自定义的信息 return new AuthUserDetails(username,encode, AuthorityUtils.commaSeparatedStringToAuthorityList(buffer.toString())); } }
在UserController中的使用:
@Override public ReturnResult login(String userName, String passWord) { System.out.println("------------- service message come in --------------"); try { User user = userService.login(userName, passWord); if (user != null) { final Authentication authentication = authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( userName, passWord ) ); SecurityContextHolder.getContext().setAuthentication(authentication); AuthUserDetails userDetails = (AuthUserDetails) userDetailsService.loadUserByUsername(userName); session.setAttribute(Contants.USER, userDetails); userDetails.setServiceSessionId(session.getId()); //此处可优化,目前只处理一个权限 Collection<GrantedAuthority> authorities = userDetails.getAuthorities(); String authorityName = ""; for (GrantedAuthority authority : authorities){ authorityName = authority.getAuthority(); } Integer roleCode = RoleEnum.getRoleCode(authorityName); List<Menus> menusList = menusService.findMenusByUserTypeAndRoleCode(user.getUserType(), roleCode); userDetails.setMenuList(menusList); return ReturnResult.resultSucc("查询成功", userDetails); } }catch (Exception e){ logger.info("认证失败:{},{}",e.getMessage(),e); } return ReturnResult.resultFail("查询失败"); }
以上就是Spring Security在SpringBoot实战中的应用、当然也可以新增一个组件继承OncePerRequestFilter类,重写doFilterInternal方法,进行接口验签。