接着说,接着说🧖♀️代码资源在下面💇♀️
上一期传送门在这里:玩转SpringBoot安全管理:SpringSecurity介绍及入门、自定义用户认证及授权管理、MVC Security安全配置介绍(内存和JDBC身份认证实现)
这一章节会比较困难🦹♂️🦹♂️🦹♂️
这里代码相对来说比较多,我就不展示啦💁♀️💁♀️
微信搜索“大魔王编程乐园”公众号,关注并发送SS身份认证 就可以得到项目源码啦🤠下期见🐱🏍
文章目录
UserDetailService身份认证
对于用户量比较大的项目来说呢,频繁使用JDBC进行数据库查询认证非常麻烦,会降低网站登陆速度,对于一个完善的项目来说,如果有一些业务已经实现了用户信息查询的服务,那么就没有必要再去使用JDBC查询了
那么现在在我们的项目里建立定义查询用户及角色信息的服务接口
1.基本工作
修改pom文件,这里不演示👱♀️
1.1 创建实体类
Authority.class
packagecom.security.domain;importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.GenerationType;importjavax.persistence.Id;importjava.io.Serializable;@Entity(name ="t_authority ")publicclassAuthorityimplementsSerializable{// 由于使用了缓存,所以一定要对实体类进行序列化@Id@GeneratedValue(strategy =GenerationType.IDENTITY)privateInteger id;privateString authority ;publicIntegergetId(){return id;}publicvoidsetId(Integer id){this.id = id;}publicStringgetAuthority(){return authority;}publicvoidsetAuthority(String authority){this.authority = authority;}@OverridepublicStringtoString(){return"Authority{"+"id="+ id +", authority='"+ authority +'\''+'}';}}
Customer .class
packagecom.security.domain;importjavax.persistence.*;importjava.io.Serializable;@Entity(name ="t_customer")publicclassCustomerimplementsSerializable{// 由于使用了缓存,所以一定要对实体类进行序列化@Id@GeneratedValue(strategy =GenerationType.IDENTITY)privateInteger id;privateString username;privateString password;privateBoolean valid;@OverridepublicStringtoString(){return"Customer{"+"id="+ id +", username='"+ username +'\''+", password='"+ password +'\''+", valid="+ valid +'}';}publicBooleangetValid(){return valid;}publicvoidsetValid(Boolean valid){this.valid = valid;}publicIntegergetId(){return id;}publicvoidsetId(Integer id){this.id = id;}publicStringgetUsername(){return username;}publicvoidsetUsername(String username){this.username = username;}publicStringgetPassword(){return password;}publicvoidsetPassword(String password){this.password = password;}}
1.2 Repository层封装
这里使用的是JPA,不是mybatisPlus,没关系,sql都比较简单,无妨
packagecom.security.repository;importcom.security.domain.Customer;importorg.springframework.data.jpa.repository.JpaRepository;publicinterfaceCustomerRepositoryextendsJpaRepository<Customer,Integer>{CustomerfindByUsername(String username);}
packagecom.security.repository;importcom.security.domain.Authority;importorg.springframework.data.jpa.repository.JpaRepository;importorg.springframework.data.jpa.repository.Query;importjava.util.List;/**
* Created by admin on 2018-11-19.
*/publicinterfaceAuthorityRepositoryextendsJpaRepository<Authority,Integer>{@Query(value ="select a.* from t_customer c,t_authority a,t_customer_authority ca where ca.customer_id=c.id and ca.authority_id=a.id and c.username =?1",nativeQuery =true)publicList<Authority>findAuthoritiesByUsername(String username);}
1.3 sql测试:通过名字返回对应的角色信息
1.4 Service层[业务方法]
packagecom.security.service;importcom.security.domain.Customer;importcom.security.domain.Authority;importcom.security.repository.CustomerRepository;importcom.security.repository.AuthorityRepository;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.redis.core.RedisTemplate;importorg.springframework.stereotype.Service;importjava.util.List;/**
* @Classname CustomerService
* @Description 对用户数据结合Redis缓存进行业务处理
*/@ServicepublicclassCustomerService{@AutowiredprivateCustomerRepository customerRepository;@AutowiredprivateAuthorityRepository authorityRepository;@AutowiredprivateRedisTemplate redisTemplate;// 业务控制:使用唯一用户名查询用户信息// 一开始先去缓存中查,查不到再去数据库查publicCustomergetCustomer(String username){Customer customer=null;Object o = redisTemplate.opsForValue().get("customer_"+username);if(o!=null){
customer=(Customer)o;}else{
customer = customerRepository.findByUsername(username);if(customer!=null){
redisTemplate.opsForValue().set("customer_"+username,customer);}}return customer;}// 业务控制:使用唯一用户名查询用户权限publicList<Authority>getCustomerAuthority(String username){List<Authority> authorities=null;Object o = redisTemplate.opsForValue().get("authorities_"+username);if(o!=null){
authorities=(List<Authority>)o;}else{
authorities=authorityRepository.findAuthoritiesByUsername(username);if(authorities.size()>0){
redisTemplate.opsForValue().set("authorities_"+username,authorities);}}return authorities;}}
1.5 UserDetailsService封装原理[重点]
UserDetailsService是security提供的进行认证用户信息封装的接口,该接口只提供了一个方法:loadUserByUsername,该方法用户通过用户名加载用户信息,使用这个接口时,需要自定义一个实现类,通过这个方法调用用户业务处理类中已有的方法进行用户详情信息封装,该方法返回类型UserDetails,它也是一个接口,用来供security认证使用
又来一波重点
由于UserDetails是一个接口,所以我们没有办法new出它的实例对象,所以在这里new的是他的一个子类User,User就是UserDetails的一个实现类,User构造方法:
它需要传入用户名、密码以及一个Collection
这里的Collection是一个权限集合,它的泛型是**GrantedAuthority[是一个接口]**,它的实现类有三个:
在这里我们使用的是第二个:SimpleGrantedAuthority
packagecom.security.service;importcom.security.domain.Authority;importcom.security.domain.Customer;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.authority.SimpleGrantedAuthority;importorg.springframework.security.core.userdetails.*;importorg.springframework.stereotype.Service;importjava.util.List;importjava.util.stream.Collectors;/**
* @Classname UserDetailsServiceImpl
* @Description 自定义一个UserDetailsService接口实现类进行用户认证信息封装
*/@ServicepublicclassUserDetailsServiceImplimplementsUserDetailsService{@AutowiredprivateCustomerService customerService;@OverridepublicUserDetailsloadUserByUsername(String s)throwsUsernameNotFoundException{// 通过业务方法获取用户及权限信息Customer customer = customerService.getCustomer(s);List<Authority> authorities = customerService.getCustomerAuthority(s);// 对用户权限进行封装List<SimpleGrantedAuthority> list = authorities.stream().map(authority ->newSimpleGrantedAuthority(authority.getAuthority())).collect(Collectors.toList());// 返回封装的UserDetails用户详情类if(customer!=null){UserDetails userDetails=newUser(customer.getUsername(),customer.getPassword(),list);return userDetails;}else{// 如果查询的用户不存在(用户名不存在),必须抛出此异常thrownewUsernameNotFoundException("当前用户不存在!");}}}
1.6 config配置类修改 [一句话搞定]
packagecom.security.config;importcom.security.service.UserDetailsServiceImpl;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.beans.factory.annotation.Qualifier;importorg.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;importorg.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;importorg.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importjavax.sql.DataSource;/**
* @program: spring security
* @description: security配置类
* @author: xmonster_大魔王
* @create: 2022-08-02 11:41
**/@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter{@Qualifier("dataSource")@AutowiredprivateDataSource dataSource;@AutowiredprivateUserDetailsServiceImpl userDetailsService;@Overrideprotectedvoidconfigure(AuthenticationManagerBuilder auth)throwsException{// 设置密码编码器BCryptPasswordEncoder bCryptPasswordEncoder =newBCryptPasswordEncoder();// 使用userDetailsService进行身份认证
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);}}
1.7 测试
先输入错误的:
正确的:❣❣❣
先来登录admin,admin的权限是common
登录李四试试~
2.总结
真香
版权归原作者 我是X大魔王 所有, 如有侵权,请联系我们删除。