简介
Spring Security 是一个提供身份验证,授权,保护以及防止常见攻击的框架,由于同时支持响应式和命令式,是spring框架的安全标准。
前提条件
jdk1.8+
例子使用SpringBoot版本 【2.0.5.RELEASE】,代码都是简写,只写关键代码,源码demo,文章底部github
引入maven jar包(正常添加jar包依赖,只需保留第一步的jarbao,第三步第四步忽略即可)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
因为SpringBoot提供了BOM来管理版本,因此不需要指定版本,如果我们需要指定版本可以通过配置maven属性来配置
例
<properties> <!-- ... --> <spring-security.version>5.2.2.BUILD-SNAPSHOT</spring-security.version> </dependencies>
- 如果使用SNAPSHOT版本则需要定义Spring Snapshot存储库
<repositories> <!-- ... possibly other repository elements ... --> <repository> <id>spring-snapshot</id> <name>Spring Snapshot Repository</name> <url>https://repo.spring.io/snapshot</url> </repository> </repositories>
- 如果使用里程碑版本或者候选的版本,则需要指定Spring Milestone存储库
<repositories> <!-- ... possibly other repository elements ... --> <repository> <id>spring-milestone</id> <name>Spring Milestone Repository</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories>
- application.properties 配置属性内容
# 启动端口 server.port=8081 # 启动项目路径 server.servlet.context-path=/simple-security
- 此时简单版的demo就已经完成了,访问http://127.0.0.1:8081会重定向到http://127.0.0.1:8081/simple-security/login 页面显示内容如下
- 默认的用户名:user,密码为控制台打印的uuid
8.此时我们输入用户名密码之后验证通过了,但是会显示404,这是因为我们没有配置登录成功的首页,现在开始配置首页
9.新建一个WebSecurityConfig配置类,继承
WebSecurityConfigurerAdapter,此处配置关闭了csrf,支持表单登录,
拦截/login登录的请求,登录成功跳转login-success
代码如下
@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { // 屏蔽csrf跨站 http.csrf().disable() // 支持表单登录 .formLogin() // 登录逻辑处理url .loginProcessingUrl("/login") // 登录页面 // .loginPage("login-view") //自定义成功登录页面 .successForwardUrl("/login-success"); } /** * 密码验证方式 * NoOpPasswordEncoder.getInstance() 字符串校验 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return NoOpPasswordEncoder.getInstance(); } }
- 配置一个WebMvcConfig,重定向我们的url,实现登录逻辑处理
@Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("redirect:/login"); } }
- 编写一个controller,作为登录成功之后的跳转
@RestController public class LoginController { @RequestMapping("/login-success") public String login(){ return "login success"; } }
12.此时启动项目,访问http://127.0.0.1:8081/simple-security/login,输入用户名user,密码(控制台打印的uuid)登录成功即可看到打印的login success
- 自定义用户,实现UserDetailsService,此处我们生成的用户名是tz,密码就是123,我们实际使用中,可以根据用户名在数据库中查找返回,此处为了方便直接设置了一下,没有进行数据库查询。这样我们就成功配置了一个用户名tz密码123的账号,现在就来登录试一下吧
@Service public class MyUserDetailService implements UserDetailsService { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { UserDetails userDetails = User.withUsername("tz").password("123").build(); return userDetails; } }
14.浏览器打开输入http://127.0.0.1:8081/simple-security/login
账号tz,密码123,点击login,登录成功跳转到我们刚才定义的controller,密码太简单了,我们平常不能直接使用明文密码啊,一般都是加密的,先看上面的WebSecurityConfig中我们定义了
PasswordEncoder的Bean 我们返回了一个
NoOpPasswordEncoder.getInstance()
这个代表进行简单的字符串匹配,也就是字符串相等就OK,我们下面配置另一个加密,BCryptPasswordEncoder(),修改我们的WebSecurityConfig
/** * 密码验证方式 * NoOpPasswordEncoder.getInstance() 字符串校验 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }
15.新建一个test类,生成我们的Bcrypt加密的密码,方便一会我们测试
@SpringBootTest public class SimpleSecurityApplicationTests { @Test public void contextLoads() { String hashpw = BCrypt.hashpw("123", BCrypt.gensalt()); System.out.println(hashpw); } // 打印结果 $2a$10$pTffBwh9mawjeGG9K5ZhbenBfWQRV1aFVgZqVt59eM67iJhDqARyG // 这个字符串就代表123,上面我们是使用的随机盐的方式,每次生成的密码都不一样,但是校验时原密码都是123 }
下面把打印的结果替换到我们的MyUserDetailService中
修改结果如下
@Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { UserDetails userDetails = User.withUsername("tz").password("$2a$10$pTffBwh9mawjeGG9K5ZhbenBfWQRV1aFVgZqVt59eM67iJhDqARyG").build(); return userDetails; }
- 启动项目,继续使用用户tz密码123登录,此时也是显示登录成功
- 下面展示一下基本的授权
Controller中新增资源url @RequestMapping("/r/r1") public String r1(){ return "r1"; } @RequestMapping("/r/r2") public String r2(){ return "r2"; }
WebSecurityConfig增加对资源的拦截,此处访问/r/r1需要p1权限,访问/r/r2需要p2权限
// 屏蔽csrf跨站 http.csrf().disable() .authorizeRequests() // 拦截/r/**请求需要认证 .antMatchers("/r/r1").hasAuthority("p1") .antMatchers("/r/r2").hasAuthority("p2") .antMatchers("/r/**").authenticated() // // 除了/r/** 其他都可以访问 .anyRequest().permitAll() .and() // 支持表单登录 .formLogin() // 登录逻辑处理url .loginProcessingUrl("/login") //自定义成功登录页面 .successForwardUrl("/login-success"); }
MyUserDetailService中给tz用户增加p1权限
@Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { UserDetails userDetails = User.withUsername("tz").password("$2a$10$pTffBwh9mawjeGG9K5ZhbenBfWQRV1aFVgZqVt59eM67iJhDqARyG").authorities("p1").build(); return userDetails; }
18.此时基本的权限拦截就完成了,如需更复杂的请参考官网验证,下面演示一下浏览器访问http://127.0.0.1:8081/simple-security/login使用用户名tz密码123来进行登录
修改为r1的访问url,访问查看我们 已经读取到r1 的资源
修改为r2的访问URL,访问查看我们已经被拒绝,显示403,因为我们只有p1权限,访问r1需要p1权限,所以我们可以访问,访问r2需要p2权限,我们没有p2权限,所以拒绝我们的访问
19. 好了入门教程到此结束了,如有错误欢迎指出
github demo地址 https://github.com/TianPuJun/springboot-demo/tree/master/springboot-simple-security
官网 https://spring.io/projects/spring-security
参考https://docs.spring.io/spring-security/site/docs/5.2.2.BUILD-SNAPSHOT/reference/htmlsingle/