springSecurity 是 一个安全框架,他是一套集合了 认证和授权的一整套框架的。
通过简单的方式来 学习spring security
通过案例 来进行学习:转自:https://www.kancloud.cn/hanxt/springsecurity/1221477
login.html登录页面,登录页面访问不受限制
在登录页面登录之后,进入index.html首页(登录验证Authentication)
首页可以看到syslog、sysuer、biz1、biz2四个页面选项
我们希望syslog(日志管理)和sysuser(用户管理)只有admin管理员可以访问(权限管理Authorization)
biz1、biz2普通的操作用户auser就可以访问(权限管理Authorization)
首先是springsecurity 的 httpBasic 模式,这种模式 实际的应用场景有限,在真实的 项目中不存在对应的应用场景,所以不过多介绍;
http.httpBasic().and()
.authorizeRequests()//任何身份
.anyRequest()//所有请求
.authenticated();//都需要认证
主要介绍spring security 的其他几种模式:
- form 表单
- form表单添加验证码
- form 表单 从数据库加载用户信息
- form表单从数据库加载权限
- 短信验证码登录
- jwt
- 第三方登录
- 提供认证管理
首先 准备工作:
springboot 2.0 + spring security + mybatis +jdk1.8+fastJson
后面也会 提供对应的代码地址
我们 要知道spring security 这个框架可以为我们做什么?
spring security 主要 可以为 我们做 认证和 鉴权的功能
认证 就是 我们常说的 登录的功能,鉴权就是我们经常做的,判断某个资源,该用户能不能访问,
认证:就是给当前的登录的用户,通过某些方式,颁发一个合理的身份和对应的权限。
鉴权:通过颁发的身份,查看当前的身份能否访问具体的方法;
认证:
在spring security 中 默认提供了:
HttpBasic模式和formLogin模式 这两种的方式
而对应的模式有对应的场景.
HttpBasic模式:是在访问具体的方法前,弹出一个框,输入的对应的用户名和对应的密码,是一种很简陋的模式,此种方式因为对应的账号密码一般都是在配合文件中写死的 或者对应的在 启动时,给定的随机的账号和密码,然后请求对应的 spring security 提供的默认方法(相当于一个servlet 不过 框架自己提供),自己配置对应的权限就可以使用,很方便。对应的安全性也是很低的。
因为他的原理就很简单,假如你在服务端配置的账号和密码是为 mbb:密码为:mbb
此时 请求到后端的时候 在对应的请求头中 就会有 这样的东西 mbb:mbb(通过base64编码),此时后端接收对应的 base64编码后的数据
package com.mbb.stu.config;
import com.mbb.stu.service.MyRBACService;
import com.mbb.stu.service.MyUserDatiailServiceImpl;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configurable
@EnableWebSecurity
//@EnableGlobalMethodSecurity(prePostEnabled = true) 启用 方法级别的 权限管理 可以 在 对应的方法上面 写对应的 spel表达式
//PreAuthorize注解 进入方法前的权限验证。示例: @PreAuthorize("hasRole('admin')") 只有拥有ADMIN角色才能访问findAll方法。以下注解都是方法上面的注解
//@PostAuthorize("returnObject.name == authentication.name") @PostAuthorize 在方法执行后再进行权限验证,适合根据返回值结果进行权限验证。
// Spring EL 提供返回对象能够在表达式语言中获取返回的对象returnObject。
// 下文代码只有返回值的name等于authentication对象的name(当前登录用户名)才能正确返回,否则抛出异常
//@PreFilter(filterTarget="ids", value="filterObject%2==0") 过滤参数的注解
//@PostFilter("filterObject.name == authentication.name") 结果过滤
public class SpringSecturityFormConfig extends WebSecurityConfigurerAdapter {
@Autowired
private SecuritySuccessHandler securitySuccessHandler;
@Autowired
private SecurityFailHandler securityFailHandler;
@Autowired
private MyUserDatiailServiceImpl myUserDatiailService;
@Autowired
private MyLogoutSuccessHandler myLogoutSuccessHandler;
@Autowired
private MyRBACService rbacService;
@Autowired
private VerificationCodeFilter verificationCodeFilter;
@Autowired
private SmsSpringSecurityConfig smsSpringSecurityConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()//禁用跨站csrf攻击防御
//在对应过滤器链中,添加对应过滤器,并且指定对应的过滤器链中 过滤器的顺序
.addFilterBefore(verificationCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin() //设置为 表单登录方式
.loginPage("/login.html")
.loginProcessingUrl("/login")//登录表单form中action的地址,也就是处理认证请求的路径
.passwordParameter("username")//表单中用户名的name
.passwordParameter("password")//表单中用户密码的name
.failureHandler(securityFailHandler) //设置自定义失败处理器
// .failureForwardUrl("/login.html")//设置认证失败跳转链接
//.defaultSuccessUrl("/index")//设置成功的跳转链接
.successHandler(securitySuccessHandler)//设置自定义 成功处理器
.and()
.apply(smsSpringSecurityConfig)
.and()
.rememberMe()//开启记住我的模式
.rememberMeParameter("remember-me")// 记住我的 复选框的 name
.rememberMeCookieName("sss")// 记住我的 cookie name
//.authenticationSuccessHandler()//记住我 成功的回调
.tokenRepository(persistentTokenRepository()) //实现 数据库 记住我的 关键 注入数据源 表的结构 还有名字 已经定义好 为 固定结构
//JdbcTokenRepositoryImpl 具体可以查看 对应的jdbc TokenRepostoryImpl 中 当然也可以 通过对应的反射 动态修改 updateTokenSql 可以自己 尝试
.and()
.logout() //开启 退出登录
.logoutUrl("/logout")//退出登录后 的请求url
// .logoutSuccessUrl("/login.html")//退出登录后 跳到的url logoutSuccessHandler 二选一
.deleteCookies("JSESSIONID")//删除的 cookies 的值 list
.logoutSuccessHandler(myLogoutSuccessHandler) //退出登录 处理类 可以自定义 退出动作登录时长计算,清理业务相关的数据等等 此处理handler 和 logoutSuccessUrl 二选一
.and()
.authorizeRequests() //设置请求
.antMatchers("/login.html","/login","/favicon.ico","/kaptcha","/smslogin","/smscode").permitAll() //匹配地址 为所有权限都可以访问的地址
.antMatchers("/index.html","/index").authenticated()
// .antMatchers("/syslog","/sysuser").hasAnyAuthority("ROLE_admin")//匹配地址 只有 admin 权限才能 访问
// .hasAnyRole("admin") //匹配地址 只有 admin 权限才能 访问 作用和上面相同 因为在 spring security 中 角色是一种特殊权限
// .antMatchers("/biz1","/biz2").hasAnyRole("admin","user")
//db下面的资源 只能是 admin + dba 的权限
// .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
//某些id 资源 只能某些用户访问
//antMatchers("/person/{id}").access("@rbacService.checkUserId(authentication,#id)")
.anyRequest().access("@rbacService.hasPermission(request,authentication)") // 此处说明 方法的参数 必须为 此处的对应 而非自定义
// access Spring El表达式 为 true 可以访问
//anonymous() 匿名 可以访问
//denyAll() 拒绝访问
//fullyAuthenticated 用户 完全认证可以访问 非 rememberMe
// hasAnyAuthority (角色也是一种特殊权限) hasAnyRole 用户拥有某种用户才可以访问
//hasIpAddress 用户来自 这个ip的可以访问
//permitAll 无任何限制
//rememberMe 允许通过 记住我 的用户访问
//authenticated 登录才可以访问
.and()
.sessionManagement() //设置session 管理器
.sessionFixation().migrateSession()
// .migrateSession() // 设置session 每次请求都是 一个新的session 并保留所有会话属性 默认的就好 其他应用场景自己归纳
// changeSessionId 变更会话ID并保留所有会话属性
// 其他选项
//none() 关闭spring security的session防护功能,spring不会配置SessionManagementFilter类;
//migrateSession() 用户认证之后,会重新创建一个新的session,并且将旧session中的属性,迁移到新的session中;
//newSession()用户认证之后,会新创建一个session,但是不会将旧的session中的属性,迁移到新的session中
.maximumSessions(1) //最大用户登录数量 就是一个账号的最大登录数
.expiredSessionStrategy(new MySessionExpiredStrategy()); //设置 session 失效的触发规则
}
/* @Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable() //禁用跨站csrf攻击防御
.formLogin()
.loginPage("/login.html")//用户未登录时,访问任何资源都转跳到该路径,即登录页面
.loginProcessingUrl("/login")//登录表单form中action的地址,也就是处理认证请求的路径
.usernameParameter("username")///登录表单form中用户名输入框input的name名,不修改的话默认是username
.passwordParameter("password")//form中密码输入框input的name名,不修改的话默认是password
.defaultSuccessUrl("/index")//登录认证成功后默认转跳的路径
.and()
.authorizeRequests()
.antMatchers("/login.html","/login").permitAll()//不需要通过登录验证就可以被访问的资源路径
.antMatchers("/biz1","/biz2") //需要对外暴露的资源路径
.hasAnyAuthority("ROLE_user","ROLE_admin") //user角色和admin角色都可以访问
.antMatchers("/syslog","/sysuser")
.hasAnyRole("admin") //admin角色可以访问
//.antMatchers("/syslog").hasAuthority("sys:log")
//.antMatchers("/sysuser").hasAuthority("sys:user")
.anyRequest().authenticated();
}*/
@Override
protected void configure(AuthenticationManagerBuilder bulider) throws Exception {
bulider.userDetailsService(myUserDatiailService)
.passwordEncoder(passwordEncoder());
/* bulider.inMemoryAuthentication().withUser("user")
.password(passwordEncoder().encode("123456"))
.roles("user")
.and()
.withUser("admin")
.password(passwordEncoder().encode("123456"))
.roles("admin")
.and()
.passwordEncoder(passwordEncoder());//配置BCrypt加密*/
}
/*public static void main(String[] args) {
System.out.println(passwordEncoder().encode("123456"));
}
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Override
public void configure(WebSecurity web) {
//将项目中静态资源路径开放出来
web.ignoring().antMatchers( "/css/**", "/fonts/**", "/img/**", "/js/**");
}
@Autowired
private DataSource dataSource;//数据源 此为 对应的 配置文件中的 数据源信息
@Bean
public PersistentTokenRepository persistentTokenRepository(){//记住我的 实体类 注入对应的 数据源
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}
}