springboot security
Authority 权限
Credential 证书
Grant 授予
Authentication 身份验证
以下,我们将通过四步,逐步实现spring-security的username+password身份验证的登录功能。
一、添加spring-boot-start-security依赖即可实现默认的username+password登录。(默认用户认证)
二、实现自定义的固定用户和密码(内存用户认证)
三、实现自定义用户及密码登录的系统(UserDetailsService认证)
四、配置自定义页面,可配置的相关页面包括:登录表单页,登录错误页,登录成功页等。
请注意,我们是以一个spring-boot-starter-web项目为起点。
一、添加spring-boot-start-security依赖即可实现默认的username+password登录。(默认用户认证)
<!-- 依赖:spring-security --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>
启动web应用,访问站点资源时,会出现spring-security提供的默认登录页面
其默认用户名为:user
登录密码在启动信息中可以找到:
填写正确即可登录成功。
这个最简单的配置适用于只有一个用户且每次系统启动后查阅更新密码的系统。当然,这种系统不常见。
二、实现自定义的固定用户和密码(内存用户认证)
需添加自定义配置,我们以java配置方式实现。
1. 创建一个继承自org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter的类;
2. 类上以@Configuration和@EnableWebSecurity注解,表明这是一个java配置类,并启用spring-security身份认证;
3. 覆写configure(AuthenticationManagerBuilder auth)方法;
4. 调用auth对象的.inMemoryAuthentication().withUser("xxx").password("xxx").roles("USER")等方法,指定用户、密码及角色,多个用户可以调用.and()方法来连接.withUser方法。
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() // 内存用户认证.withUser("xxx").password("xxx").roles("USER") // 配置用户xxx密码xxx及角色USER .and() .withUser("yyy").password("yyy").roles("USER") // 配置用户yyy密码yyy及角色USER
; } }
重启web应用后,默认的user用户登录方式已失效,
现在可以用户xxx或yyy登录(针对spring4版本)
使用spring5版本的话,在此,会报错:java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null",登录页面无法跳转
spring5要求必须指定密码编译码器,我们可以用BCryptPasswordEncoder。
修改一下configure(AuthenticationManagerBuilder auth)方法,填加一行代码:.passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式
@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() // 内存用户认证.passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式.withUser("xxx").password("xxx").roles("USER") // 配置用户xxx密码xxx及角色USER .and() .withUser("yyy").password("yyy").roles("USER") // 配置用户yyy密码yyy及角色USER ; }
重新登录,还出错:
控制台有提示:Encoded password does not look like BCrypt(看上去不像BCrypt编码的密码)
我们的密码"xxx"是以明文方式传递的,用new BCryptPasswordEncoder().encode("xxx")改为密文即可。
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication() // 内存用户认证.passwordEncoder(new BCryptPasswordEncoder()) // 指定加密方式.withUser("xxx").password(new BCryptPasswordEncoder().encode("xxx")).roles("USER") // 配置用户xxx密码xxx及角色USER .and().withUser("yyy").password(new BCryptPasswordEncoder().encode("yyy")).roles("USER") // 配置用户yyy密码yyy及角色USER ;}}
以上是修改后的配置类,再次重启登录就正常了。
这个简单的配置适用于拥有少数明确固定用户且密码不得改变的系统。当然,这种系统不够灵活。
三、实现自定义用户及密码登录的系统(UserDetailsService认证)
1. 依然使用上面的配置类;
2. 只是调用auth的.userDetailsService方法,该方法需要一个UserDetailsService接口作为参数;
3. 需要实现UserDetailsService接口的loadUserByUsername(String username):UserDetails方法来完成用户身份认证;
loadUserByUsername(String username)返回一个UserDetails接口;
UserDetails接口要求提供用户名、密码、角色等属性信息;
4. 注意指定密码编译器,可参考前例。
我们用一个私有方法来提供UserDetails接口作为示例,实际运用时推荐调用一个实体服务方法(例如:UserService.findBy(String username):User)来提供UserDetails接口。示例代码如下:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(new UserDetailsService(){@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return findBy(username); //findBy(username)仅是一个示例方法//return UserService.findByName(username); //通常,应该用一个Serice来实现 }// 示例方法findBy(username)private UserDetails findBy(String username) {return new UserDetails(){private String username = "aaa"; //假定用户名为aaaprivate String password = "aaa"; //假定用户密码为aaa @Overridepublic String getUsername() {return username;}@Overridepublic String getPassword() {// 注意在此返回指定密码编译器编译的密文密码return new BCryptPasswordEncoder().encode(password);}//以上属性通常是自定义实体类User定义的//以下属性是User实现UserDetails接口必须的 @Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return java.util.Arrays.asList(new SimpleGrantedAuthority("USER"));//默认为USER角色}@Overridepublic boolean isAccountNonExpired() {return true; //默认账户未过期 }@Overridepublic boolean isAccountNonLocked() {return true; //默认用户未被锁定 }@Overridepublic boolean isCredentialsNonExpired() {return true; //默认证书未过期 }@Overridepublic boolean isEnabled() {return true; //默认有效,即用户未被停用 }};}}).passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 ;}}
配置类经过以上修改,再次重启,以用户aaa密码aaa就正常登录了。
需注意事项:
1. Arrays来自java.util.Arrays;
2. spring5中必须指定密码编译器,.passwordEncoder(new BCryptPasswordEncoder())
3. new SimpleGrantedAuthority("USER")只是示例性的简单授权,实际应用中应以数据源来提供用户角色。
4. 如果我们另以MyUserDetailsService实现UserDetailsService接口的话,代码更清晰,实际上主体代码是:
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter{@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth .userDetailsService(new MyUserDetailsService()) .passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 ;} }
以上为UserDetailsService认证的java配置类主体代码。
这种通过实现UserDetailsService接口来完成用户身份认证的方式基本可以满足绝大部分系统需求,即username+password认证方式。
至此,功能倒是实现了,但spring-security提供的默认登录页面,未免过于简陋,这些界面是可以定制的。
四、配置自定义页面,可配置的相关页面包括:登录表单页,登录错误页,登录成功页等。
依然使用上面的配置类,重写configure(HttpSecurity http)方法来配置自定义页面。
以登录页为例,我们以spring-boot-starter-web和Thymeleaf来做示例。
前例中使用spring-security默认的登录页url为:http://localhost:8080/login,登录错误页url为:http://localhost:8080/login?error=true。
1. 对应的,我们设计自定义的url:登录http://localhost:8080/uia/login,登录错误http://localhost:8080/uia/login?error=true来取代。
2. 定义一个控制器来响应上面的登录请求
3. 编写登录页面,默认的输入参数name分别为username和password,自定义的话可以用类似.usernameParameter("usr").passwordParameter("pwd")方法来指定;
4. 修改配置类,重写configure(HttpSecurity http)方法,若啥也不写的话,默认配置为匿名访问所有资源。
5. 配置开放静态资源(/res/**)及及登录等(/uia/**),而其他请求都得认证;
6. 配置登录表单等请求;
7. 为简化复杂性,忽略csft防范!!! 切记,生产环境中不可忽略。spring4+默认启用csft,会拦截所有的POST请求,表现为提交登录表单无效。
@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/css/**", "/uia/**").permitAll() //开放静态资源和登录等页面.anyRequest().authenticated() //其他所有资源均需认证 .and().formLogin() // 启用登录表单.loginPage("/uia/login") //指定登录请求.failureUrl("/uia/login?error=true") //指定登录失败请求//.usernameParameter("usr").passwordParameter("pwd") //指定form中输入域input的name属性.and().csrf().disable() //示例中为简化复杂性,忽略csft防范!!!切记,此项不可用于生产环境。 ;}
Controller简单示例,其他如注册,找回密码等代码已移除。
@RestController @RequestMapping("/uia") public class UiaController {@RequestMapping(value="/login")public ModelAndView login(){//TODO 处理些页面后台数据,例如:登录提示、错误原因等return new ModelAndView("loginView");}@RequestMapping("/logout")public ModelAndView logout(HttpServletRequest request, HttpServletResponse response){Authentication auth = SecurityContextHolder.getContext().getAuthentication();if (auth != null){ new SecurityContextLogoutHandler().logout(request, response, auth);}return new ModelAndView("loginView");} }
登录页loginView.html简单示例,在此使用了Thymeleaf模板,多余的如bootstrap代码均已移除。
!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="utf-8"> <title>Login</title> </head> <body><h2>Please login</h2><form name="f" method="post" th:action="@{/uia/login}" ><input type="text" name="username" placeholder="user name" /><input type="password" name="password" placeholder="password" /><input type="submit" value="login" /> </form><p><span th:if="${param.error}">登录失败</span></p> </body> </html>
注意:
form的action属性被th:action="@{/uia/login}所替代,这样,在post提交时,会自动补上sessionId。
最后,贴上完成的SecurityConfig.java代码
1 @Configuration 2 @EnableWebSecurity 3 public class SecurityConfigB extends WebSecurityConfigurerAdapter{ 4 5 @Override 6 protected void configure(HttpSecurity http) throws Exception { 7 http 8 .authorizeRequests() 9 .antMatchers("/css/**", "/uia/**").permitAll() //开放静态资源和登录等页面 10 .anyRequest().authenticated() //其他所有资源均需认证 11 .and() 12 .formLogin() // 启用登录表单 13 .loginPage("/uia/login") //指定登录请求 14 .failureUrl("/uia/login?error=true") //指定登录失败请求 15 //.usernameParameter("usr").passwordParameter("pwd") //指定form中输入域input的name属性 16 .and().csrf().disable() //示例中为简化复杂性,忽略csft防范!!!切记,此项不可用于生产环境。 17 ; 18 } 19 20 @Override 21 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 22 auth.userDetailsService(new UserDetailsService(){ 23 24 @Override 25 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 26 return findBy(username); //findBy(username)仅是一个示例方法 27 //return UserService.findByName(username); //通常,应该用一个Serice来实现 28 } 29 30 // 示例方法findBy(username) 31 private UserDetails findBy(String username) { 32 return new UserDetails(){ 33 34 private String username = "aaa"; //假定用户名为aaa 35 private String password = "aaa"; //假定用户密码为aaa 36 37 @Override 38 public String getUsername() { 39 return username; 40 } 41 42 @Override 43 public String getPassword() { 44 // 注意在此返回指定密码编译器编译的密文密码 45 return new BCryptPasswordEncoder().encode(password); 46 } 47 48 //以上属性通常是自定义实体类User定义的 49 //以下属性是User实现UserDetails接口必须的 50 51 @Override 52 public Collection<? extends GrantedAuthority> getAuthorities() { 53 return java.util.Arrays.asList(new SimpleGrantedAuthority("USER")); 54 } 55 56 @Override 57 public boolean isAccountNonExpired() { 58 return true; //默认账户未过期 59 } 60 61 @Override 62 public boolean isAccountNonLocked() { 63 return true; //默认用户未被锁定 64 } 65 66 @Override 67 public boolean isCredentialsNonExpired() { 68 return true; //默认证书未过期 69 } 70 71 @Override 72 public boolean isEnabled() { 73 return true; //默认有效,即用户未被停用 74 }}; 75 }}) 76 .passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 77 ; 78 } 79 }
请自行实现UserService服务,启用下面第27行代码:
把配置文件变成这样
@Configuration @EnableWebSecurity public class SecurityConfigC extends WebSecurityConfigurerAdapter{@AutowiredUserService userService;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/res/**", "/uia/**").permitAll() //开放静态资源和登录等页面.anyRequest().authenticated() //其他所有资源均需认证 .and().formLogin() // 启用登录表单.loginPage("/uia/login") //指定登录请求.failureUrl("/uia/login?error=true") //指定登录失败请求//.usernameParameter("usr").passwordParameter("pwd") //指定form中输入域input的name属性//.and().csrf().disable() //示例中为简化复杂性,忽略csft防范!!!切记,此项不可用于生产环境。 ;}@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(new UserDetailsService(){@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userService.findByName(username); //通常,应该用一个Service来实现 }}).passwordEncoder(new BCryptPasswordEncoder()) // 指定密码编译器 ;} }
至此,spring-security也算可以用起来了。
转载于:https://www.cnblogs.com/godwithus/p/9127755.html
springboot security相关推荐
- springboot+security整合(1)
说明 springboot 版本 2.0.3 源码地址:点击跳转 系列 springboot+security 整合(1) springboot+security 整合(2) springboot+s ...
- springboot+security框架整合
springboot+security框架整合 springboot项目搭建大家可以去请教度娘,有很多文章,这里主要讲解springboot和security安全框架的集成,因为springmvc跟s ...
- springboot+security实例
springboot+security pom文件 <?xml version="1.0" encoding="UTF-8"?> <proje ...
- 视频教程-SpringBoot+Security+Vue前后端分离开发权限管理系统-Java
SpringBoot+Security+Vue前后端分离开发权限管理系统 10多年互联网一线实战经验,现就职于大型知名互联网企业,架构师, 有丰富实战经验和企业面试经验:曾就职于某上市培训机构数年,独 ...
- SpringBoot Security 自定义登录验证逻辑+密码加盐
密码加盐思路 JAVA 加盐加密方法_Teln_小凯的博客-CSDN博客 盐加密方法 @ApiOperation(value = "002-加密")@PreAuthorize(&q ...
- springboot +security +mybatis+thymeleaf 实现简单的用户 角色 权限(资源) 管理
1.用户 角色 资源的关系 2.实现思路 3.参考资料 Spring Boot Security +Redis 实现简单权限控制 将返回结果变成json 响应改客户端 在第六项 4.实现代码 ...
- springboot security 权限校验_springboot借助aop和注解实现权限校验
我们用springboot做后台开发,难免会用到权限校验,比如查看当前用户是否合法,是否是管理员.而spring的面向切面的特效可以帮助我们很好的实现动态的权限校验.这里我们就用到的spring的ao ...
- springboot security 权限不足_SpringBoot 整合 SpringSecurity 之起源篇(零)
本篇为SpringSecurity的第一篇,主要来介绍下什么是SpringSecurity,以及在springboot中如何使用它 I. 基本知识点 官方文档: https://docs.spring ...
- springboot security 权限校验_十二、SpringBoot 优雅的集成Spring Security
前言 至于什么是Spring security ,主要两个作用,用户认证和授权.即我们常说的,用户只有登录了才能进行其他操作,没有登录的话就重定向到登录界面.有的用户有权限执行某一操作,而有的用户不能 ...
- springBoot+security+mybatis 实现用户权限的数据库动态管理
[b][size=large]一.Spring Security 应用的概述[/size][/b] [size=medium] 鉴于目前微服务的兴起,Spring周边方案的普及,以及 Spring S ...
最新文章
- 中医大计算机应用基础考试题6,中医大 计算机应用基础 复习题 参考资料.docx
- 阿里云使用笔记-MySQL远程连接-centos7
- 反思快速在新项目中找字段的方法
- 算法导论读书笔记(8)
- Linux内核工程师是怎么步入内核殿堂的?
- arm linux 蜂鸣器qt,Qt 程序中使用蜂鸣器 ioctl()
- binwalk 提取bootimg_boot.img的解包与打包
- 分享两条关于Eclipse Perl插件EPIC的tips吧~
- 《算法图解》第四章课后作业
- 3.3 CMMI3级——技术解决方案(Technical Solution)
- Qt视频播放器界面Demo
- html播放rtmp直播,video.js实现浏览器播放rtmp协议直播流的问题
- Python学习笔记之蓝牙模块通讯-Pybluez
- json格式校验(json格式校验器)
- TuX2:用于机器学习的分布式图计算
- 中小公司IT经理面试指南
- Json及Jsoncpp开源库的应用
- 【LaTeX】论文写作之参考文献(数模、美赛、学位论文、英文SCI论文写作通用)
- 最受欢迎的Python开源框架有哪些?
- Go语言Seelog入门
热门文章
- BZOJ_1629_[Usaco2007_Demo]_Cow_Acrobats_(贪心)
- printf的缓存问题
- 基于nodej脚手架express-generator,生成express项目
- 「批处理」以服务方式启动批处理
- Apache vs Nginx vs Tomcat vs JBoss vs Jetty
- CF1041F Ray in the tube构造_思维
- redis-3.0.0_rc5的RPM包制定
- nyoj 4 ASCII码排序(set,multiset)
- Sharing A Powerful Tool For Calculate Code Lines
- C#反序列化XML异常:在 XML文档(0, 0)中有一个错误“缺少根元素”