SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源)

本文主要是介绍SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

欢迎你来看我的SpringSecurity系列文章,因为是系列文章,所以部分文章内容是有承前启后的关系

这个UserDetailsService接口是用户数据的来源,就是登录用户的数据从哪里来,是从数据库来?还是从你电脑的TXT文本上过来的,还是从其他地方,该接口会返回UserDetails接口的实例,而这个UserDetails接口,就是用户信息,因为用户信息除了username,password这些字段之外,不同项目会包含不同的信息,比如在我的项目中,用户登录之后会在用户信息中添加age,birthday等信息,所以spring定义了一个UserDetails接口,就是为了方便我们自定义字段的

任何实现了UserDetailsService接口的实现类,都可以作为认证数据的来源,只要把这个实现类的实例注入到spring容器中,SpringSecurity便可以自动发现并使用该实例

所以本篇文章将演示:用户shiwentian想访问test1,但是需要登录,用户输入帐号shiwentian,密码123456,通过UserDetailsService接口拿到该帐号的用户,匹配帐号密码,成功登陆,然后将用户的username,age,birthday等信息放到UserDetails接口实例,转换成json字符串,最终返回到前台页面,然后由前端发起请求访问test1,当然了,本文不会展示前端发送请求的代码

步骤1: 首先我们定义一个controller,里面包含一个方法

@RestController
public class AController {@RequestMapping("/test1")public String test1() {return "OK1";}
}

步骤2: 然后定义一个用户类,上文也说了,因为我们要返回给前端age和birthday,所以这个类必须我们自己实现UserDetails接口


public class TestUser implements UserDetails {// 注意,这个password是需要加密之后,再set到本类的,本文后续会有代码再次提示该注意事项private String password;// username是spring security默认的账号字段名字private String username;// 正是因为存在下面2个字段,所以我们需要自己实现UserDetails,spring自己提供了// 几个UserDetails接口的实现,但是我们要返回给前端age和birthday,所以不用spring的private int age;		private String birthday;@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}// 注意:下面3个方法如果返回false,则spring security会登录失败并且在错误信息中提示"用户已失效"// 错误信息在AuthenticationFailureHandler接口的AuthenticationException.getMessage()方法中// 错误信息文章请参考本系列文章的第4章节:AuthenticationFailureHandler接口@Overridepublic boolean isAccountNonExpired() {// 本文为了演示,恒定写死返回truereturn true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}// 如果此方法返回false,则spring security会登录失败并且在错误信息中提示"用户帐号已被锁定"// 错误信息在AuthenticationFailureHandler接口的AuthenticationException.getMessage()方法中// 错误信息文章请参考本系列文章的第4章节:AuthenticationFailureHandler接口@Overridepublic boolean isAccountNonLocked() {// 本文为了演示,恒定写死返回truereturn true;}// 省略了其他重写方法,省略了部分get/set方法,为了篇幅更短
}

步骤3: 接下来用户类有了,但是用户类里有一个比较特殊的字段,就是password,在spring security中,它要求我们先根据帐号,查询到这个用户,然后spring security自己拿着前端页面传递过来的密码进行一次加密,spring加密完前端密码之后,会用这个加密后的密码,和我们上述代码返回的TestUser类的password对比,所以这里遇到的问题就是:spring security需要知道加密方式是什么
spring是这样做的,如果容器中有PasswordEncoder接口实例,则直接使用该接口实例进行加密,如果容器中不存在PasswordEncoder接口实例,则系统直接报错,错误信息为:There is no PasswordEncoder mapped for the id "null",所以我们自己往容器中注入一个PasswordEncoder,代码如下:

@Service
public class TestPasswordEncoder extends BCryptPasswordEncoder {// 这种写法叫做Full Configuration Class
}

在这里我多说一下,上述中的TestPasswordEncoder 直接继承了spring自带的BCryptPasswordEncoder ,其实更简单的方法应该是下面这样的,在启动类或者某个@Service或者@Component等等这种类中使用@Bean注解

@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}// 写这个就不用写上面的TestPasswordEncoder了// 这种写法叫做Lite Configuration Class@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

我为什么要自己写一个TestPasswordEncoder 呢?主要是我突然想到了full模式和lite模式,也没什么,就是复习一下这两个模式而已。。。你也可以参考这篇ConfigurationClassUtils类复习一下,主要就是lite模式适用于已有的类,但是没有注入到容器中,例如本文中的BCryptPasswordEncoder

步骤4: 接下来实现UserDetailsService接口,用于查询登录的帐号密码是否正确


@Service
public class TestUserDetailsService implements UserDetailsService {// 上文已经注入了PasswordEncoder,所以我们直接注入到本类,当然,在代码里new一个也行,因为加密规则都相同,无所谓@Resourceprivate PasswordEncoder encoder;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 如果输入的帐号是shientian,则模拟从数据库查询出该账号的密码if ("shiwentian".equals(username)) {TestUser user = new TestUser();user.setUsername("史文天");user.setPassword(encoder.encode("123456"));// 模拟从数据库查询出密码user.setAge(28);user.setBirthday("1996-03-12");return user;}// 如果输入的帐号不是shiwentian,则直接返回null,表示没有该账号,此时spring security会提示帐号密码不匹配return null;
}

步骤5: 帐号密码正确,登录成功,将该人员信息返回给前端页面

public class TestReturnJsonValueHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {// 主要注意一下getPrincipal方法,它返回的就是本文开头的TestUser类,也就是UserDetails接口的实现类Object testUser = authentication.getPrincipal();response.setContentType("application/json;charset=UTF-8");PrintWriter out=response.getWriter();ObjectMapper jsonObj = new ObjectMapper();String jsonString = jsonObj.writeValueAsString(testUser);out.write(jsonString);out.close();// authentication.getAuthorities();}
}

步骤6: 启动服务,浏览器输入http://localhost:8081/test1,此时会跳转到登录页面,输入帐号shiwentian,密码123456,则浏览器上显示该人员的Json字符串格式的信息

本文到此就结束了,在spring security中,有UserDetailsService接口是有几个实现类的,所以我们可以不自己实现,例如查询数据库可以直接使用JdbcUserDetailsManager,它就是一个UserDetailsService接口

下一篇文章:菜单接口权限

这篇关于SpringSecurity-5-UserDetailsService与UserDetails接口(用户来源)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057389

相关文章

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

usb接口驱动异常问题常用解决方案

《usb接口驱动异常问题常用解决方案》当遇到USB接口驱动异常时,可以通过多种方法来解决,其中主要就包括重装USB控制器、禁用USB选择性暂停设置、更新或安装新的主板驱动等... usb接口驱动异常怎么办,USB接口驱动异常是常见问题,通常由驱动损坏、系统更新冲突、硬件故障或电源管理设置导致。以下是常用解决

Java中Switch Case多个条件处理方法举例

《Java中SwitchCase多个条件处理方法举例》Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,:本文主要介绍Java中SwitchCase多个条件处理的相... 目录前言基本语法处理多个条件示例1:合并相同代码的多个case示例2:通过字符串合并多个case进阶用法使用

Mysql用户授权(GRANT)语法及示例解读

《Mysql用户授权(GRANT)语法及示例解读》:本文主要介绍Mysql用户授权(GRANT)语法及示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql用户授权(GRANT)语法授予用户权限语法GRANT语句中的<权限类型>的使用WITH GRANT

Java中的Lambda表达式及其应用小结

《Java中的Lambda表达式及其应用小结》Java中的Lambda表达式是一项极具创新性的特性,它使得Java代码更加简洁和高效,尤其是在集合操作和并行处理方面,:本文主要介绍Java中的La... 目录前言1. 什么是Lambda表达式?2. Lambda表达式的基本语法例子1:最简单的Lambda表

Java中Scanner的用法示例小结

《Java中Scanner的用法示例小结》有时候我们在编写代码的时候可能会使用输入和输出,那Java也有自己的输入和输出,今天我们来探究一下,对JavaScanner用法相关知识感兴趣的朋友一起看看吧... 目录前言一 输出二 输入Scanner的使用多组输入三 综合练习:猜数字游戏猜数字前言有时候我们在

Spring Security+JWT如何实现前后端分离权限控制

《SpringSecurity+JWT如何实现前后端分离权限控制》本篇将手把手教你用SpringSecurity+JWT搭建一套完整的登录认证与权限控制体系,具有很好的参考价值,希望对大家... 目录Spring Security+JWT实现前后端分离权限控制实战一、为什么要用 JWT?二、JWT 基本结构

java解析jwt中的payload的用法

《java解析jwt中的payload的用法》:本文主要介绍java解析jwt中的payload的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解析jwt中的payload1. 使用 jjwt 库步骤 1:添加依赖步骤 2:解析 JWT2. 使用 N

springboot项目如何开启https服务

《springboot项目如何开启https服务》:本文主要介绍springboot项目如何开启https服务方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录springboot项目开启https服务1. 生成SSL证书密钥库使用keytool生成自签名证书将

Java实现优雅日期处理的方案详解

《Java实现优雅日期处理的方案详解》在我们的日常工作中,需要经常处理各种格式,各种类似的的日期或者时间,下面我们就来看看如何使用java处理这样的日期问题吧,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言一、日期的坑1.1 日期格式化陷阱1.2 时区转换二、优雅方案的进阶之路2.1 线程安全重构2