spring security简单教程以及实现彻底先后端分离

spring security是spring家族的一个安全框架,入门简单。对比shiro,它自带登陆页面,自动完成登陆操做。权限过滤时支持http方法过滤。html

在新手入门使用时,只须要简单的配置,便可实现登陆以及权限的管理,无需本身写功能逻辑代码。前端

可是对于如今大部分先后端分离的web程序,尤为是前端广泛使用ajax请求时,spring security自带的登陆系统就有一些不知足需求了。java

由于spring security有本身默认的登陆页,本身默认的登陆控制器。而登陆成功或失败,都会返回一个302跳转。登陆成功跳转到主页,失败跳转到登陆页。若是未认证直接访问也会跳转到登陆页。可是若是前端使用ajax请求,ajax是没法处理302请求的。先后端分离web中,规范是使用json交互。咱们但愿登陆成功或者失败都会返回一个json。何况spring security自带的登陆页太丑了,咱们仍是须要使用本身的。git

 

spring security通常简单使用:github

web的安全控制通常分为两个部分,一个是认证,一个是受权。web

认证管理:ajax

就是认证是否为合法用户,简单的说是登陆。通常为匹对用户名和密码,即认证成功。spring

在spring security认证中,咱们须要注意的是:哪一个类表示用户?哪一个属性表示用户名?哪一个属性表示密码?怎么经过用户名取到对应的用户?密码的验证方式是什么?数据库

只要告诉spring security这几个东西,基本上就能够了。编程

import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { }

事实上只要继承WebSecurityConfigurerAdapter ,spring security就已经启用了,当你访问资源时,它就会跳转到它本身默认的登陆页。可是这还不行,

当用户点击登陆时,

1.它会拿到用户输入的用户名密码;

2.根据用户名经过UserDetailsService 的 loadUserByUsername(username)方法得到一个用户对象;

3.得到一个UserDetails 对象,得到内部的成员属性password;

4.经过PasswordEncoder 的 matchs(s1, s2) 方法对比用户的输入的密码和第3步的密码;

5.匹配成功;

 

因此咱们要实现这三个接口的三个方法:

1.实现UserDetailsService ,能够选择同时实现用户的正常业务方法和UserDetailsService ;

例如:UserServiceImpl implement IUserService,UserDetailsService {}

2.实现UserDetails ,通常使用用户的实体类实现此接口。

其中有getUsername(), getPassword(), getAuthorities()为获取用户名,密码,权限。可根据我的状况实现。

3.实现PasswordEncoder ,spring security 提供了多个该接口的实现类,可百度和查看源码理解,也能够本身写。

三个实现类的配置以下:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(NoOpPasswordEncoder.getInstance()); } }

其中Userdetails 为UserDetailsService 中 loadUserByUsername() 方法的返回值类型。

 

到目前为止,就能够完成简单认证了。而受权管理,到如今,是默认的:全部资源都只有‘认证’权限,全部用户也只有‘认证’权限。即,通过认证就能够访问全部资源。

以上,就是spring security的简易应用。能够实现一个稍微完整的安全控制。很是简单。

 

受权管理:

受权管理,是在已认证的前提下。用户在认证后,根据用户的不一样权限,开放不一样的资源。

根据RBAC设计,用户有多个角色,角色有多个权限。(真正控制资源的是权限,角色只是一个权限列表,方便使用。)

每一个用户都有一个权限列表,受权管理,就是权限和资源的映射。在编程中,写好对应关系。而后当用户请求资源时,查询用户是否有资源对应的权限决定是否经过。

权限写在数据库,配置文件或其余任何地方。只要调用loadUserByUsername()时返回的UserDetails对象中的getAuthorities()方法能获取到。

因此不管用户的权限写在哪里,只要getAuthorities()能获得就能够了。

举例:

受权管理映射:add==/api/add,query==/api/query;

数据库中存储了用户权限:query;

那么该用户就只能访问/api/query,而不能访问/api/add。

受权管理配置以下:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.crypto.password.NoOpPasswordEncoder; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(NoOpPasswordEncoder.getInstance()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add") .antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query") .antMatchers("/home").hasAuthority("base"); } }

以上就是spring security的基本应用。下面是解决先后端分离下的没法302跳转的状况。

需求是:先后端分离,须要本身的登陆页面,使用ajax请求。

出现问题:本身的登陆页面请求登陆后,后端返回302跳转主页,ajax没法处理;未认证请求资源时,后端返回302跳转登陆页,也没法处理。

解决思想:修改302状态码,修改成401,403或者200和json数据。

 

HttpSecurity 有不少方法,能够看一看

好比 设置登陆页(formLogin().loginPage("/login.html")) 能够设置本身的登陆页(该设置主要是针对使用302跳转,且有本身的登陆页,若是不使用302跳转,先后端彻底分离,无需设置)。

好比 设置认证成功处理

好比 设置认证失败处理

好比 设置异常处理

好比 设置退出成功处理

能够继承重写其中的主要方法(里面有httpResponse对象,能够随便返回任何东西)

例如:

import org.springframework.http.HttpStatus; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class LoginSuccessHandler implements AuthenticationSuccessHandler { @Override public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.setStatus(HttpStatus.OK.value()); } }

设置完成登陆成功和失败处理后,仍是不够知足需求,当用户未经过登陆页进入网站,咱们须要在用户直接访问资源时,告诉前端此用户未认证。(默认是302跳转到登陆页)。咱们能够改为返回403状态码。

这里就须要实现一个特殊的方法:AuthenticationEntryPoint 接口的 commence()方法。

这个方法主要是,用户未认证访问资源时,所作的处理。

spring security给咱们提供了不少现成的AuthenticationEntryPoint 实现类,

好比默认的302跳转登陆页,好比返回403状态码,还好比返回json数据等等。固然也能够本身写。和上面的登陆处理同样,实现接口方法,将实现类实例传到配置方法(推荐spring注入)。

以下:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.crypto.password.NoOpPasswordEncoder; import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint; @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Qualifier("userService") @Autowired private UserDetailsService userDetailsService; @Autowired private LoginSuccessHandler loginSuccessHandler; @Autowired private LoginFailureHandler loginFailureHandler; @Autowired private MyLogoutHandler logoutHandler; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(NoOpPasswordEncoder.getInstance()); } @Override protected void configure(HttpSecurity http) throws Exception { http .formLogin() .loginProcessingUrl("/login") // 登陆成功
 .successHandler(loginSuccessHandler) // 登陆失败
 .failureHandler(loginFailureHandler).permitAll() .and() // 注销成功
 .logout().logoutSuccessHandler(logoutHandler) .and() // 未登陆请求资源
                .exceptionHandling().authenticationEntryPoint(new Http403ForbiddenEntryPoint()) .and() .authorizeRequests() .antMatchers(HttpMethod.POST, "/api/data").hasAuthority("add") .antMatchers(HttpMethod.GET, "/api/data").hasAuthority("query") .antMatchers("/home").hasAuthority("base"); } }

以上就算是完了,前端发起ajax请求时,后端会返回200,401,403状态码,前端可根据状态码作相应的处理。

如下是个人所有代码(后端,安全管理demo)

https://github.com/Question7/spring-security-demo

相关文章
相关标签/搜索