Why SpringSecurity?
在 Web 应用开发中,安全
一直是非常重要的一个方面。安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。如果在应用开发的后期才考虑安全的问题,就可能陷入一个两难的境地:一方面,应用存在严重的安全漏洞,无法满足用户的要求,并可能造成用户的隐私数据被攻击者窃取;另一方面,应用的基本架构已经确定,要修复安全漏洞,可能需要对系统的架构做出比较重大的调整,因而需要更多的开发时间,影响应用的发布进程。
Spring
是一个非常流行和成功的 Java 应用开发框架。Spring Security
基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)
和用户授权(Authorization)
两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码
。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作
。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
对于上面提到的两种应用情景,Spring Security 框架都有很好的支持。在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。
MAVEN依赖
由于Spring Boot提供Maven BOM来管理依赖版本,因此无需指定版本。
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
开源项目
以下代码已经发布在spring-cloud-study-security项目,可以直接下载使用。
核心代码
CustomFromLoginFilter
自定义表单登陆过滤:
-
读取
页面穿过来的username和password的参数值 -
校验
账号密码是否通过,不通过则报错 - 通过则授予
USER
权限
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义表单登录
* @author zhengkai.blog.csdn.net
*/
public class CustomFromLoginFilter extends AbstractAuthenticationProcessingFilter {
CustomFromLoginFilter(String defaultFilterProcessesUrl) {
super(new AntPathRequestMatcher(defaultFilterProcessesUrl, HttpMethod.POST.name()));
}
@Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
//读取页面穿过来的username和password
String username = httpServletRequest.getParameter("username");
String password = httpServletRequest.getParameter("password");
//校验账号密码是否通过
customCheck(username, password);
//授予USER权限
List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>();
simpleGrantedAuthorities.add(new SimpleGrantedAuthority("USER"));
return new UsernamePasswordAuthenticationToken(username, password, simpleGrantedAuthorities);
}
private void customCheck(String username, String password){
//校验用户名为user密码为pass
if (!("user".equals(username) && "pass".equals(password))){
throw new RuntimeException("用户名或密码错误!");
}
}
}
WebSecurityConfig
Security配置:
-
.antMatchers("/login","/index","/logout").permitAll()
,设置这些路径无需授权允许访问。 -
.formLogin().successForwardUrl("/user/list")
,设置支持表单登陆,登陆成功后跳转到user/list的url路径 -
http.addFilterAt(customFromLoginFilter(), UsernamePasswordAuthenticationFilter.class);
设置自定义过滤器,类型为账号密码授权过滤器。
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.web.authentication.UsernamePasswordAuthenticationFilter;
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
/**
* 匹配 "/" 路径,不需要权限即可访问
* 匹配 "/user" 及其以下所有路径,都需要 "USER" 权限
* 退出登录的地址为 "/logout",退出成功后跳转到页面 "/login"
* 默认启用 CSRF
* @author zhengkai.blog.csdn.net
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/login","/index","/logout").permitAll()
.antMatchers("/user/**").hasAuthority("USER")
.and()
.formLogin().successForwardUrl("/user/list")
.and()
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
http.addFilterAt(customFromLoginFilter(), UsernamePasswordAuthenticationFilter.class);
}
/**
* 自定义认证过滤器
*/
private CustomFromLoginFilter customFromLoginFilter() {
return new CustomFromLoginFilter("/login");
}
}
UserController
这里需要设置几个页面:
-
index
默认页面,不需要授权,可以点击登陆 -
login
登陆页面,不需要授权,进行登陆 -
user/list
需要授权才可访问页面,必须登陆
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
@RestController
public class UserController {
@GetMapping("index")
public ModelAndView index(){
return new ModelAndView("index");
}
@GetMapping("login")
public ModelAndView login(String error){
return new ModelAndView("login").addObject("error",error);
}
@GetMapping("user/list")
public ModelAndView userList(){
return new ModelAndView("user-list");
}
}
HTML页面
index.html
<h1>Index Html</h1>
<p>for security check.please <a href="${request.contextPath}/login">login</a></p>
login.html
<h1>Login Page</h1>
<form method="post" action="${request.contextPath}/login">
<input id="username" name="username" placeholder="username" type="text">
<input id="password" name="password" placeholder="password" type="password">
<button type="submit">Login</button>
</form>
user-list.html
<h1>User List | Security Page</h1>
<p>U r under security state if u can see the page</p>
效果演示