首页 > 其他分享 >Spring Security(3)

Spring Security(3)

时间:2022-11-23 08:44:40浏览次数:34  
标签:自定义 Spring user Override new Security public

您好,我是湘王,这是我的博客园,欢迎您来,欢迎您再来~

 

前面运行写好的代码之所以没有任何显示,是因为还没有对Spring Security进行配置,当然啥也不显示了。这就好比你坐在车上,却不打开发动机,车子当然跑不起来。所以咱们就来让它跑起来。不过在配置之前,有必要对Spring Security的登录流程做个大致了解。

如果深入源码去了解,这个玩意及其复杂,但是没必要,知道它的机制就行了。就好比你买车也不必把发动机拆开去看它是怎么工作的吧。简单来说它就是下面这些步骤:

1、Spring Security通过AuthenticationManager接口进行身份验证

2、ProviderManager是AuthenticationManager的一个默认实现

3、ProviderManager把验证工作委托给了AuthenticationProvider接口

4、AuthenticationProvider的实现类DaoAuthenticationProvider会检查身份认证

5、DaoAuthenticationProvider又把认证工作委托给了UserDetailsService接口

6、自定义UserDetailsService类从数据库中获取用户账号、密码、角色等信息,然后封装成UserDetails返回

7、使用Spring Security还需要自定义AuthenticationProvider接口,获取用户输入的账号、密码等信息,并封装成Authentication接口

8、将UserDetails和Authentication进行比对,如果一致就返回UsernamePasswordAuthenticationToken,否则抛出异常

下面是认证流程图:

 

 

 

首先重写loadUserByUsername:

/**
 * 自定义用户详情
 *
 * @author 湘王
 */
@Service("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRoleService userRoleService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Collection<GrantedAuthority> authorities = new ArrayList<>();
        // 从数据库中取出用户信息
        SysUser user = userService.getByName(username);
        // 判断用户是否存在
        if(null == user) {
            System.out.println("user is not exist");
            throw new UsernameNotFoundException("user is not exist");
        }

        // 获得用户角色:方式一
        List<SysUserRole> list = userRoleService.getByUserId(user.getId());
//        // 获得用户角色:方式二
//        List<SysRole> list = roleService.getByUserId(user.getId());

//        // 给用户添加授权:方式一
//        for (SysUserRole userRole : list) {
//            SysRole role = roleService.getById(userRole.getRoleid());
//            authorities.add(new SimpleGrantedAuthority(role.getName()));
//        }
//        // 返回UserDetails实现类
//        return new User(user.getName(), user.getPassword(), authorities);

        // 给用户添加授权:方式二
        return User
                .withUsername(username)
                .password(user.getPassword())
                .authorities(list.stream()
                                    .filter(Objects::nonNull)// 判断是否为空
                                    .map(userRole -> roleService.getById(userRole.getRoleid()))// 从SysUserRole获取Role
                                    .map(SysRole::getName)// 转变为角色名称字符串
                                    .map(SimpleGrantedAuthority::new)// 依据角色名称创建SimpleGrantedAuthority
                                    .toArray(SimpleGrantedAuthority[]::new)// list转变为数组
                ).build();
    }
}

 

 

 

因为UserDetailsService返回了封装的UserDetails,所以需要再自定义AuthenticationProvider返回Authentication接口:

/**
 * 自定义登录验证
 *
 * @author 湘王
 */
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取表单输入中返回的用户名
        String username = (String) authentication.getPrincipal();
        // 获取表单中输入的密码
        String password = (String) authentication.getCredentials();
        // 这里调用我们的自己写的获取用户的方法
        UserDetails userInfo = customUserDetailsService.loadUserByUsername(username);
        if (userInfo == null) {
            System.out.println("user is not exist");
            throw new UsernameNotFoundException("user is not exist");
        }

        PasswordEncoder passwordEncoder = new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }
            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return s.equals(charSequence.toString());
            }
        };

        // 采用简单密码验证
        if (!passwordEncoder.matches(password, userInfo.getPassword())) {
            System.out.println("user or password error");
            throw new BadCredentialsException("user or password error");
        }

        Collection<? extends GrantedAuthority> authorities = userInfo.getAuthorities();
        // 构建返回的用户登录成功的token
        return new UsernamePasswordAuthenticationToken(userInfo, password, authorities);
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

 

接着来实现实现WebSecurityConfigurerAdapter,它通过重写WebSecurityConfigurerAdapter中的相关方法(一般是configurer)来自定义配置。WebSecurityConfigurerAdapter主要做几件事:

1、初始化

2、开启Security

3、配置各种过滤器,实现验证过滤器链

 

下面是它的代码:

/**
 * spring security验证配置
 *
 * @author 湘王
 */
// 配置类
@Configuration
// 开启Security服务
@EnableWebSecurity
// 开启全局Securtiy注解
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Autowired
    private CustomAuthenticationProvider authenticationProvider;

    // 自定义的登录验证逻辑
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider);
    }
    // 控制逻辑
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 执行UsernamePasswordAuthenticationFilter之前添加拦截过滤
        http.addFilterBefore(new CustomInterceptorFilter(), UsernamePasswordAuthenticationFilter.class);

        http.authorizeRequests()
            .anyRequest().authenticated()
            // 设置自定义认证成功、失败及登出处理器
            .and().formLogin().loginPage("/login")
            .and().cors()
            .and().csrf().disable();
    }
    @Override
    public void configure(WebSecurity web) throws Exception {
        // 设置拦截忽略文件夹,可以对静态资源放行
        web.ignoring().antMatchers("/css/**", "/js/**");
    }
}

 

 

接着用postman进行测试:

 

 

 

回顾整个调用过程,它的时序图是:

 

 

 

但是等等:好像除了/login,其他方法都不能正常访问!

 

 


 

 

感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~

 

标签:自定义,Spring,user,Override,new,Security,public
From: https://www.cnblogs.com/xiangwang1111/p/16916724.html

相关文章

  • Spring面相切片编程的配置。
    AOP面向切面配置:1、context:component-scan:扫描包路径下的所有类注解。<!--指定扫描com.sfwu15.bean包下的所有类的注解注意:扫描包时,会扫描所有包下的子孙包--><......
  • Grafana+OpenSearch+Spring Boot集成(一) 【基础环境配置】
    先决条件1、环境Windows,内存最好在8GB以上已安装DockerDesktop(下载地址:https://www.docker.com/products/docker-desktop/)2、知识准备了解如何使用Docker了解Op......
  • Spring Cloud LoadBalancer 如何指定服务使用指定负载均衡策略
    当系统中有多个服务A,B,C时默认使用轮询策略当我们A服务需要使用指定IP策略时只需要在springboot代码中使用注解@LoadBalancerClients(value={@LoadBalancerClient......
  • Spring Security笔记
    这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战SpringSecurity简介SpringSecurity是一种高度自定义的安全框架,利用(基于)SpringIOC/DI和AOP功能,为系统......
  • 基于springboot和vue的IT内部电脑报修服务系统设计与实现-计算机毕业设计源码+LW文档
    it内部设备服务系统设计与实现摘要it内部设备服务系统将传统的网络服务方式与最新的互联网技术相结合,使用方便快捷,有利于设备维修部门规范管理,提高网络维修部门的工作效......
  • 基于springboot“漫画之家”系统设计与实现-计算机毕业设计源码+LW文档
    摘要随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生,各行各......
  • Spring Data(数据)Redis
    版本3.0.0SpringDataRedis项目通过使用键值样式数据存储将核心Spring概念应用于解决方案的开发。我们提供了一个“模板”作为发送和接收消息的高级抽象。您可能会注......
  • Spring Data(数据)R2DBC
    版本3.0.0SpringDataR2DBC项目将Spring的核心概念应用于开发使用关系数据库R2DBC​驱动程序的解决方案。我们提供了用于存储和查询行的高级抽象。​​DatabaseClient​......
  • 关于Spring注解的基础详解(补充上次并不清楚的内容)
    注解,需要在.xml文件里面加这么一句话:<context:component-scanbase-package=""/>(组件)Component注解主要用于接口的实现类里面,代替掉.xml文件里面的这句话:(主要作用:代替......
  • 再次打开Spring界面,多处报错
    分享一下经历在我再次打开Srpring之后,打算解决一下“历史遗留问题”,发现多处标红(挺崩溃的)!就比如这句话,刚才就是不亮:毕竟我上次的应用还是很顺利的,所以也就没有第一时间......