首页 > 其他分享 >SpringSecurity初学总结

SpringSecurity初学总结

时间:2024-09-15 13:49:50浏览次数:3  
标签:总结 return private 认证 userDetailsDTO 初学 Override SpringSecurity public

springSecurity 安全框架
    基于Java 的安全框架主要有:Spring Security 和 Shiro

    介绍基础概念
        安全框架是对用户访问权限的控制,保证应用的安全性。
            其主要的工作是用户认证 和 用户授权|鉴权
        主要应用于 Spring 的企业应用系统,提供声明式的安全访问控制解决方案。
            它提供了一组可以在 Spring 应用上下文中配置的Bean
            能很好的结合 Spring 的DI 依赖注入和 AOP 面向切面编程功能

应用场景 ------单点登录   SSO(Single Sign On)
    在分布式项目中会包含多个子项目,每个子项目都会接入认证和授权
    需要实现用户只认证一次便可以在多个子项目中访问时不用再次认证
    这个功能就叫做单点登陆。

从本质上讲,springSecurity是一组过滤器(称为过滤器链),依赖于Servlet过滤器,过滤器链
    它们能够拦截客户的请求,并将这些请求转给认证管理器(AuthenticationManager)认证
    和访问决策管理器处理(AccessDecisionManager) 来确定是否有权限访问

核心组件
    ConSecurityContext ----安全上下文 ,主要持有Aurhentication 对象
    SecurityContextHolder -------- 它持有的是安全上下文的信息。
    UserDetails  ------ 提供用户信息     (接口)
    Authentication ------- 主体认证(信息)  (接口)

我对security 的一些理解

        其实UserDetails 是 springSecurity 提供的一个认证对象的接口,就是说,如果你要认证的是User,你就在 实现类里 封装进去。        

        UserDetailsService 认证服务,需要去实现最终认证的一个逻辑,在配置类中与认证提供者(AuthenticationProvider)相绑定。

流程上,可以分为 登录 ,请求认证/授权

     在认证过滤器中(一般)通过判断 请求头中是否携带 token,来决定是否拦截,如果没有则认为未登录( ConSecurityContext 不会发生变化 )。

     放行交给后续的过滤器,到了某个过滤器中,如果 SecurityContext 没有发生变化,则生成一个匿名的认证令牌,添加到 SecurityContext ,放行到请求的登录方法中。

     (此处以 账号+密码 登录为例)用账号和密码生成 一个未认证的令牌,调用认证管理者的认证方法( authManager.authenticate(token) ) 进行认证,这个方法的底层源码,认证管理者会先使用Iterator 迭代获取出所有的认证提供者, 与认证提供者进行匹配,看看是否有认证提供者能够提供认证服务,去进行认证,认证成功后在提供者创建一个认证成功的信息类返回。其实其中还有经过许多步骤。

请求认证/授权

        主要是对已经登录的用户请求其他资源时的流程。也是会进行认证,但是是生成未认证令牌放在SecurityContext 中进行一个认证。授权会去遍历用户所拥有的权限,去与明文配置的权限进行比对,或者是使用决策访问管理者(AccessDecisionManager)来做一个是否放行的判断,决定用户是否可以请求该资源。

配置类

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    // ----------------------装配区-------------------------
    @Resource
    private DaoAuthenticationProvider provider; //如果后续有多种登录方式,需要配置多个认证提供者
    @Resource(name = "JWT")
    private OncePerRequestFilter jwtAuthFilter; //jwt认证过滤器
    @Resource
    private AuthenticationEntryPoint authEntryPoint; //登录异常消息处理器
    @Resource
    private AccessDeniedHandler accessDeniedHandler; //权限异常消息处理器
    @Autowired
    private UserDetailsService service;  //用户信息认证服务
    @Autowired
    private PermMapper permMapper;


    //-----------------------配置组件bean-----------------------------------

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean  //注意生成密码时 还没有设置加密编码 --- 已设置
    public DaoAuthenticationProvider daoAuthenticationProvider(UserDetailsService service){
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); //创建编码器
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); //创建认证提供者
        provider.setPasswordEncoder(encoder); //设置认证提供者编码器
        provider.setUserDetailsService(service);  //将认证提供者与认证服务绑定
        return provider;
    }

    @Override //将认证提供者 加入 认证管理器
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(provider);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.authorizeRequests()
                .antMatchers("/Common/login","/Common/register","/Common/loginWithBody").anonymous()
                .antMatchers("/Common/welcome","/User/getMenu").permitAll();

        setAuthentication(http);

        http.addFilterBefore(jwtAuthFilter,
                UsernamePasswordAuthenticationFilter.class);

        http.exceptionHandling()
                .accessDeniedHandler(accessDeniedHandler)
                .authenticationEntryPoint(authEntryPoint);
    }


    private void setAuthentication( HttpSecurity http ) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry EIR = http.authorizeRequests();
        for (Permission permission : permMapper.getAllChild()) {
            EIR.antMatchers( permission.getMapping() ).hasAuthority(permission.getName());
            System.out.println( permission.getName() +"   " + permission.getMapping() );
        }
        EIR.anyRequest().authenticated();
    }

}

UserDetailsServiceImpl

@Service
public class UserDetailsServiceImpl implements UserDetailsService {



    //由于将两次查表,密码的加密全都封装到 UserService 中的 getDTOByUsername(username);
    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDetailsDTO dto = userService.getDTOByUsername(username);
        if( dto == null ){
             throw new UsernameNotFoundException("用户不存在");
        }

        return new UserDetailsImpl(dto);  //调试 成功
    }
}

jwt认证过滤器

@Component(value = "JWT")
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Resource
    private RedisTemplate<String,Object> template;


    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse resp, FilterChain filterChain) throws ServletException, IOException {
        String token = req.getHeader("token");

        if(token == null){ //没有token 直接放行交给后面的过滤器处理
            filterChain.doFilter(req,resp);
            return;
        }
        //解析失败抛异常,放行交给后面的过滤器处理

        User user = null;
        user = JwtUtils.parseToken(token);
        String KEY = SecurityUtils.makeKEY(user);
        //判断redis 中的数据是否过期
        Long expire = template.getExpire(KEY);
        if( expire == null || expire == -2){
            System.out.println("数据已经过期");
            filterChain.doFilter(req,resp);
            return;
        }

        //续时 ,取出
        ValueOperations<String, Object> op = template.opsForValue();
        UserDetailsDTO dto = (UserDetailsDTO)op.get(KEY);
        op.set(KEY,dto,60*5, TimeUnit.SECONDS);
        UserDetails userDetails = new UserDetailsImpl(dto);
        //生成认证对象
        Authentication authToken =
                new UsernamePasswordAuthenticationToken
                        (userDetails, null, userDetails.getAuthorities());
        //放入上下文中 进行认证
        SecurityContextHolder.getContext().setAuthentication(authToken);
        filterChain.doFilter(req,resp);
    }
}

LoginServiceImpl

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    private RedisTemplate<String,Object> template;
    @Autowired
    private AuthenticationManager authManager;

    @Autowired
    private TokenTemplate tokenTemplate;

    @Override
    public User login(String username, String password) {

        //使用用户名加密码生成鉴别令牌
        Authentication token =
                new UsernamePasswordAuthenticationToken(username, password);
        //调用鉴别管理器 进行鉴别 , 返回一个已鉴别的令牌
        Authentication authenticate = authManager.authenticate(token);
        //获取 认证对象
        UserDetailsImpl details = (UserDetailsImpl)authenticate.getPrincipal();
        //获取 dto
        UserDetailsDTO dto = details.getUserDetailsDTO();
        // 生成KEY  将dto 存入 redis ,key 为 生成的 KEY
        tokenTemplate.saveTokenRedis(dto);
        return dto.getUser();
    }
}

UserDetailsImpl

public class UserDetailsImpl implements UserDetails {
    private  UserDetailsDTO userDetailsDTO;

    private List<GrantedAuthority> authorities ;  //所有的权限列表

    // ----------------------构造器方法------------------------
    public UserDetailsImpl() {}

    public UserDetailsImpl(UserDetailsDTO userDetailsDTO) {
        this.userDetailsDTO = userDetailsDTO;
        setAuthorities();
    }

    //--------------------------方法----------------
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    public void setUserDetailsDTO(UserDetailsDTO userDetailsDTO) {
        this.userDetailsDTO = userDetailsDTO;
    }

    //数据也从数据库中获取
    public void setAuthorities() {
        List<String> perms = userDetailsDTO.getPerms();
        List<GrantedAuthority> list = new ArrayList<>();
        perms.forEach(perm -> {
            list.add(new SimpleGrantedAuthority(perm));
        });
        userDetailsDTO.getUser().getRoles().forEach( role ->{
            list.add(new SimpleGrantedAuthority(role.getName())); //已经 使用role.getName() 获取出角色名
        } );
        //--------------调错测试------------
        list.forEach(System.out::println);
        this.authorities = list;

    }

    public UserDetailsDTO getUserDetailsDTO() {
        return userDetailsDTO;
    }

    @Override
    public String getPassword() {
        return userDetailsDTO.getUser().getPassword();
    }

    @Override
    public String getUsername() {
        return userDetailsDTO.getUser().getUsername();
    }


    //后面可以根据账号字段的值来动态控制
    @Override
    public boolean isAccountNonExpired() {

        return true;
    }//账号是否不过期

    @Override
    public boolean isAccountNonLocked() {

        return true;
    }//账号是否不锁定

    @Override
    public boolean isCredentialsNonExpired() {

        return true;
    }//账号 凭证(密码)是否不过期

    @Override
    public boolean isEnabled() {

        return true;
    }//账号是否启用
}

标签:总结,return,private,认证,userDetailsDTO,初学,Override,SpringSecurity,public
From: https://blog.csdn.net/weixin_66442229/article/details/142225801

相关文章

  • Mysql 面试题总结
    1.Mysql数据库,隔离级别有哪几个?在MySQL数据库中,事务的隔离级别决定了一个事务在执行期间对其他事务可见的数据变化情况。MySQL支持SQL标准定义的四种隔离级别,从低到高依次为:读未提交(READUNCOMMITTED)在该隔离级别下,事务中的修改即使没有提交,对其他事务也是可见的。......
  • 数学建模常用模型---“算法”总结(含特性和应用场景)
    目录数学建模常用模型算法总结1.代数模型(AlgebraicModels)2.微分方程模型(DifferentialEquationModels)3.概率模型(ProbabilisticModels)4.优化模型(OptimizationModels)5.统计模型(StatisticalModels)6.机器学习模型(MachineLearningModels)7.网络和图论模型(Network......
  • 算法工程师重生之第二天(长度最小的子数组 螺旋矩阵II 区间和 开发商购买土地 总结 )
    参考文献代码随想录一、长度最小的子数组给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl,numsl+1,...,numsr-1,numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。示......
  • 关于鸿蒙开发框架,页面搭建样式语法总结
    鸿蒙中的变量/常量创建采用了ts语法我们在声明变量的同时需要指定变量类型,定义变量时也是要遵守变量命名规范:    1.只能包含数字、字母、下划线、$,不能以数字开头…重点    2.不能使用内置关键字或保留字,比如let、const    3.严格区分大小写1.变......
  • 2024.9.13 总结(考 DP)
    (实际上是2024.9.14写的)本来以为是考DS的。()T1题目里给的那个性质可能是来干扰的。异或上右移一位的数,其实就是除了第一位(最左边的),算贡献的时候都要看这一位异或前一位是不是1。于是随便线性DP,状态里记一下当前位填0还是1就行了。DP数组状态的第一维可以不要,省空......
  • 【大模型专栏—进阶篇】语言模型创新大总结——“后起之秀”
    大模型专栏介绍......
  • 【大模型专栏—进阶篇】语言模型创新大总结——“三派纷争”
    大模型专栏介绍......
  • AOP失效场景总结
    AOP(面向切面编程)在Spring中是通过动态代理机制来实现的,但在某些情况下,AOP可能会失效。以下是常见的几种AOP失效的场景及原因:1.内部方法调用原因:当类中的一个方法调用同一个类中的另一个方法时,AOP不会生效。解释:SpringAOP是基于代理的实现,只有通过代理对象调用方法时,......
  • 【鸿蒙应用】总结一下ArkUI
    ArkUI是HarmonyOS应用界面的UI开发框架,提供了简洁的UI语法、UI组件、动画机制和事件交互等等UI开发基础,以此满足应用开发者对UI界面开发的需求。组件是界面搭建的最小单位,开发者通过多种组件的组合构成完整的界面。页面是ArkUI最小的调度分隔单位,开发者可以将应用设计为......
  • k8s面试总结
    k8s是一个Google开源的的容器编排工具。k8s能够实现弹性伸缩、负载均衡、版本回退等功能。一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。下面,以部署一个nginx服务来说明kubernetes系统各个组件调用关系:首先要明确,一旦kub......