Spring Security 是一个安全框架,能够为 Spring企业应用系统提供声明式的安全访问控制。 主要用来做访问权限管理
Spring Security 基于 Servlet 过滤器、 IoC和AOP , 为 Web 请求和方法调用提供身份确认和授权处理,避免了代码耦合,减少了大量重复代码工作。
它的两大功能? 认证和授权
知道了它是什么了,那么接下来我们就看看怎么用?
首先我们这个是解耦的,基于javaConfig的开发模式,所以可以直接建一个Config类
大致随便看一个模板,拿来体验体验,不要求看懂,后面我们来细讲
//AOP切面编程,加个config就可以实现功能,不用改变代码
//WebSecurityConfigurerAdapter 自定义Security策略
//EnableWebSecurity 开启WebSecurity模式,@Enablexxxxx开启某个功能
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//链式编程
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可以访问,功能页只有对应有权限的人才能访问
//请求授权的规则
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限默认会到登录页,需要开启登录页面,定制登录页
//可以直接把表单的提交地址改为Controller里面去主页的路径
//loginProcessingUrl对应表单请求的路径,但是要求默认传值的name为username,password
//可以利用.usernameParameter("user").passwordParameter("pwd")改默认的参数name值
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
//开启注销功能
//logoutUrl 你要去哪里注销
// http.logout().logoutSuccessUrl("/");
//防止网站工具,post ,登陆失败可能的原因,但是我没降级用的 Springsecurity5 没问题
http.csrf().disable();
http.logout().logoutSuccessUrl("/");
//开启记住我的功能 cookie默认2周,自定义接收前端记住我的参数
http.rememberMe().rememberMeParameter("remember");
}
//认证,springboot.2.1.x,可以使用
//密码编码:passwordEncoder
//在Spring Security 5.0+ 新增了加密方法
//AuthenticationManagerBuilder 自定义认证策略
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常应该从数据库里读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123"))
.roles("vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123"))
.roles("vip1","vip2","vip3").and()
.withUser("youke").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
;
}
}
首先我们想要使用就必要引入的依赖
<!-- security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
当然如果是整合前端模板Themeleaf的话,还要引入两个依赖
<!--thymeleaf模板-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<!--SpringSecurity+thymeleaf-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
带有http的参数是授权方法
@EnableWebSecurity //开启Security支持
public class SpringConfig extends WebSecurityConfigurerAdapter {
@Override //必须重写的方法,一个配置参数的方法
protected void configure(HttpSecurity http) throws Exception{
}
}
//这个是写在上面configure方法里面的
//首页所有人可以访问,功能页只有对应有权限的人才能访问
//请求授权的规则
http.authorizeRequests().antMatchers("/").permitAll() //允许所有人可以访问/路劲
.antMatchers("/level1/**").hasRole("vip1") //要拥有vip1权限的人才能访问/level1/**的路径,下面照着解读
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
没有权限默认会到登录页,需要开启登录页面,定制登录页
http.formLogin() 加了这个就会让没有认证的人去登录页面
//loginPage 去哪个页面登录
//loginProcessingUrl对应登录表单请求的路径,但是要求前台默认传值的name为username,password
//如果不想写loginProcessingUrl,那么去哪个页面登录的那个表单action地址就要和这个loginPage路劲一样
//可以利用.usernameParameter("user").passwordParameter("pwd")改默认的参数username和password值
http.formLogin().loginPage("/toLogin").loginProcessingUrl("/login");
http.logout(); //开启注销
http.logout() 开启注销 ,他会自动删除cookie那些
值得注意的是登录登出,你开启的他的基本方法如果没有配置,就会走他自带的登录登出页面
http.logout() .logouturl("/logout") ; 这里就定义了去哪个页面注销
http.logout() .logoutSuccessUrl("/index"); 这里是自动注销了然后跳转去index页面
开启记住我
//开启记住我的功能 cookie默认2周,自定义接收前端记住我的参数
http.rememberMe().rememberMeParameter("remember");
带有AuthenticationMannagerBuilder的是认证
共同点都是,密码要加密 Security5.0后都要加密,而且为了安全最好加密密码 BCryptPasswordEncoder或者MD5之内的加密方式
//认证,springboot.2.1.x,可以使用
//密码编码:passwordEncoder
//在Spring Security 5.0+ 新增了加密方法
//AuthenticationManagerBuilder 自定义认证策略
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常应该从数据库里读
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123"))
.roles("vip2","vip3").and().withUser("root").password(new BCryptPasswordEncoder().encode("123"))
.roles("vip1","vip2","vip3").and()
.withUser("youke").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
;
}
上面withuser就是认证user这些人的信息,并且给予权限roles
当然如果是数据库的话我们就需要再自己建一个类UserDetailService
接口重写loadUserByUsername
方法,固定写法,模板拿来即用
@Component
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserInfoService userInfoService;
/**
* 需新建配置类注册一个指定的加密方式Bean,或在下一步Security配置类中注册指定
*/
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 通过用户名从数据库获取用户信息,这里就是调用数据库的接口
UserInfo userInfo = userInfoService.getUserInfo(username);
if (userInfo == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 得到用户角色
String role = userInfo.getRole();
// 角色集合
List<GrantedAuthority> authorities = new ArrayList<>();
// 角色必须以`ROLE_`开头,数据库中没有,则在这里加
authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
return new User(
userInfo.getUsername(),
// 因为数据库是明文,所以这里需加密密码
passwordEncoder.encode(userInfo.getPassword()),
authorities
);
}
}
然后得到了用户信息之后就可以接轨上面的configure方法了
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDatailService userDatailService; //固定注入这个
/**
* 指定加密方式
*/
@Bean
public PasswordEncoder passwordEncoder(){
// 使用BCrypt加密密码
return new BCryptPasswordEncoder();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
// 从数据库读取的用户进行身份认证
.userDetailsService(userDatailService) //用户信息
.passwordEncoder(passwordEncoder()); //用户加密的密码
}
}
上面设置完后,重新启动,在登录页面就可以输入数据库中的用户名/密码了。
随着登录的用户,伴随的权限管理
thymeleaf为例
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
sec:authorize="!isAuthenticated() 如果还没有登录,就显示登录按钮
sec:authorize="isAuthenticated() 如果登录了显示用户名和注销
sec:authentication=“name” 显示用户名
sec:authentication=“principal.authorities” 显示用户权限
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon">i> 登录
a>
div>
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名:<span sec:authentication="name">span>
角色:<span sec:authentication="principal.authorities">span>
a>
<a class="item" th:href="@{/logout}">
<i class="sign- out icon">i> 注销
a>
div>
那如何实现模块div之间对应权限的隐藏呢?
sec:authorize=“hasRole(‘vip1’)” 可以根据拥有此权限的用户才显示
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon">i> Level-1-1a>div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon">i> Level-1-2a>div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon">i> Level-1-3a>div>
div>
div>
div>
div>
之前说了认证的时候要加密密码为了安全,这里还值得一题的是csrf防脚本攻击
//防止网站工具,post ,登陆失败可能的原因,但是我没降级用的 Springsecurity5 没问题 http.csrf().disable();
看到了这里差不多都理解了基本用法,接下来就是做一个小案例了,
提醒一下如果是前后端分离的项目,需要注意跨域问题
一个简单的跨域,可以之间在授权里面做
@Override
protected void configure(HttpSecurity http) throws Exception {
// 允许跨域访问
http.cors();
}
这样就可以实现跨域了