SpringSecurity(安全)
在web开发中,安全第一位!过滤器,拦截器~
做网站:安全应该在什么时候考虑?设计之初
- 漏洞,隐私泄露
- 架构一旦确定
shiro,springsecurity:很像~除了类不一样,名字也不一样
认证,授权(user,vip,svip),如实现超级会员和会员以及普通用户区分这一需求
- 功能权限
- 访问权限
- 菜单权限
- …拦截器,过滤器:大量的原生代码~冗余
SpringSecurity整合了
MVC-Spring-Springboot-框架思想
实现了Aop:横切~配置类以及实现了拦截过滤的作用,可以用于权限管理
简介
Spring Security是一个提供身份验证,授权和保护以防止常见攻击的框架。凭借对命令式和响应式应用程序的一流支持,它是用于保护基于Spring的应用程序的事实上的标准。
Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。像所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求
记住几个类:
- WebSecurityConfigurerAdapter:自定义Security策略
- AuthenicationManagerBuilder:自定义认证策略
- @EnableWebSecurity:开启WebSecurity模式
SpringSecurity的两个主要目标是“认证”和“授权”(访问控制)
特征
- 对身份验证和授权的全面且可扩展的支持
- 防止攻击,例如会话固定,点击劫持,跨站点请求伪造等
- Servlet API集成
- 与Spring Web MVC的可选集成
使用方法
文档里有很多使用方法,我们这使用springboot的。
在springboot的Maven项目中使用的启动器名字为:spring-boot-starter-security(其他方法参考上面文档地址,如果访问不到自己租买加速器吧)
pom.xml里导入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
使用时,创建一个java配置类SecurityConfig继承WebSecurityConfigurerAdapter并且加上**@EnableWebSecurity**开启MVC security安全支持,然后在idea使用快捷键AIT+INSERT选择Override Methods选择重写方法,或者直接Ctrl+o可以直接看见这个父类Web安全适配器的方法有哪些,里面configure有哪个方法使用,程序通过调用他们进行重载完成授权和认证。这里面最重要的就是其中两个方法一个是参数为HttpSecurity http的方法和AuthenticationManagerBuilder auth的方法。
故继承之后==》
1.重写configure(HttpSecurity http)方法,进行用户授权管理
2.重写configure(AuthenticationManagerBuilder auth)方法,进行自定义用户认证
当然如果感觉重写方法无从下手,在idea中请我们可以选择参数的类型使用快捷键Ctrl+B进入方法,可以看见http或者auth的父类有有哪些方法可以用,看见idea提示说下载源码,点击下载源码这样可以看见注释,注释里有方法使用的样例还有英文注解。
两个方法都采取的是链式的表达式。
相对通用的模板
大家结合上面方法点进源码模仿写:
以下给出一个比较通用的模板:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.*;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.security.web.savedrequest.SavedRequest;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
@EnableWebSecurity // 开启MVC security安全支持
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Value("${COOKIE.VALIDITY}")
private Integer COOKIE_VALIDITY;
/**
* 重写configure(HttpSecurity http)方法,进行用户授权管理
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 1、自定义用户访问控制
http.authorizeRequests()
.antMatchers("/","/page/**","/article/**","/login").permitAll()
.antMatchers("/back/**","/assets/**","/user/**","/article_img/**").permitAll()
.antMatchers("/admin/**").hasRole("admin")
.anyRequest().authenticated();
// 2、自定义用户登录控制
http.formLogin()
.loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.successHandler(new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
String url = httpServletRequest.getParameter("url");
// 获取被拦截的原始访问路径
RequestCache requestCache = new HttpSessionRequestCache();
SavedRequest savedRequest = requestCache.getRequest(httpServletRequest,httpServletResponse);
if(savedRequest !=null){
// 如果存在原始拦截路径,登录成功后重定向到原始访问路径
httpServletResponse.sendRedirect(savedRequest.getRedirectUrl());
} else if(url != null && !url.equals("")){
// 跳转到之前所在页面
URL fullURL = new URL(url);
httpServletResponse.sendRedirect(fullURL.getPath());
}else {
// 直接登录的用户,根据用户角色分别重定向到后台首页和前台首页
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
boolean isAdmin = authorities.contains(new SimpleGrantedAuthority("ROLE_admin"));
if(isAdmin){
httpServletResponse.sendRedirect("/admin");
}else {
httpServletResponse.sendRedirect("/");
}
}
}
})
// 用户登录失败处理
.failureHandler(new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
// 登录失败后,取出原始页面url并追加在重定向路径上
String url = httpServletRequest.getParameter("url");
httpServletResponse.sendRedirect("/login?error&url="+url);
}
});
// 3、设置用户登录后cookie有效期,默认值
http.rememberMe().alwaysRemember(true).tokenValiditySeconds(COOKIE_VALIDITY);
// 4、自定义用户退出控制
http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
// 5、针对访问无权限页面出现的403页面进行定制处理
http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
// 如果是权限访问异常,则进行拦截到指定错误页面
RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher("/errorPage/comm/error_403");
dispatcher.forward(httpServletRequest, httpServletResponse);
}
});
}
/**
* 重写configure(AuthenticationManagerBuilder auth)方法,进行自定义用户认证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 密码需要设置编码器
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
// 使用JDBC进行身份认证
String userSQL ="select username,password,valid from t_user where username = ?";
String authoritySQL ="select u.username,a.authority from t_user u,t_authority a," +
"t_user_authority ua where ua.user_id=u.id " +
"and ua.authority_id=a.id and u.username =?";
auth.jdbcAuthentication().passwordEncoder(encoder)
.dataSource(dataSource)
.usersByUsernameQuery(userSQL)
.authoritiesByUsernameQuery(authoritySQL);
}
}
一些坑
接下来就是一些注意使用注意事项:
如果使用SpringSecurity和themleaf整合记得加入整合包以及前端写的时候,sec:authorize="hasRole(‘admin’)"sec属性写在href跳转标签前面才能生效。整合包也要注意版本,springboot新版本就导入新的,2.3.1可用包如下:
<!-- thymeleaf模板整合security控制页面安全访问依赖 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
前端写法:
<a class="header-info" sec:authorize="hasRole('admin')" href="/admin">后台管理</a>
退出功能,前端可以在上面模板下可以直接在前端使用loginout,而不需要写Controller方法,这个是本框架自带的模板,即上代码在http里的这一段
// 4、自定义用户退出控制
http.logout().logoutUrl("/logout").logoutSuccessUrl("/");
同时由于默认开启CSRF,所以记住做这些操作的时候要用表单标签以及post,不要用get方法,
<form name="logoutform" th:action="@{/logout}" method="post"></form>
又或者在html的标签中指定Token和请求头Header是带有_csrf的,还有Ajax请求也一样!!!望注意!!!
登录功能如果不想设置为login而是让其他走其他页面为login的话,如使用tologin代码如下:
http.formLogin()
.loginPage("/tologin")
.loginProcessingUrl("login")
.usernameParameter("username").passwordParameter("password")
同时注意.usernameParameter(“username”).passwordParameter(“password”)这两个参数也是默认参数,这里需要和你前端的name参数一致,这里也是坑,一不留神就浪费大量的时间,请记住!!