系列学习安全框架 Security 之第 1 篇 —— 认识 SpringSecurity

什么是 SpringSecurity?

Spring Security 是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

百度百科地址:https://baike.baidu.com/item/spring%20security/8831652?fr=aladdin

SpringCloud Security 是基于 Spring Security 为基础而开发的,因此我们学习了 SpringSecurity 就可以移植到 SpringCloud Security 了,详见中文社区网:https://www.springcloud.cc/

 

Security 应用场景

Security 在很多企业中作为后台角色权限框架、授权认证 Oauth2.0 、安全防护(防止跨站点请求)、Session攻击、非常容易融合SpringMVC使用等。

 

先下载本篇博客代码:https://pan.baidu.com/s/1R0Daoh7Zdw1-DQKrLYAbkw   提取码:pshm

本篇代码例子,主要是一个入门的 demo:有2个角色 admin 和 user,其中 admin 有增删改查权限,user 只有读的权限。通过整合 SpringSecurity 和 SpringBoot 来做权限控制,很多代码目前先写固定,主要是为了方便学习,后续会从数据库中读取。

SpringBoot 版本号:2.2.2.RELEASE,SpringCloud 版本号:Hoxton.SR2

首先,代码结构:

在 pom.xml 里,我们需要增加 security 的依赖和 freemarket 的依赖,主要做一些页面模板:

        <!-- springboot整合freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <!-->spring-boot 整合security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

然后,templates 目录下的文件都比较简单,使用的是 freemarker 模板,需要提一点的就是 login 文件,用户名和密码是与 security 要求的字段名一致,然后在地址里判断是否有 error 而判断是登录失败,实际项目开发,不应该这样判断,目前是为了学习需要。

 

然后,WebServerAutoConfiguration 配置类,主要是为了自定义 WEB 服务器参数 可以配置默认错误页面。

package com.study.config;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;

/**
 * @author biandan
 * @description 自定义 WEB 服务器参数 可以配置默认错误页面
 * @signature 让天下没有难写的代码
 * @create 2021-05-30 下午 9:28
 */
@Configuration
public class WebServerAutoConfiguration {

    @Bean
    public ConfigurableServletWebServerFactory webServerFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        ErrorPage errorPage400 = new ErrorPage(HttpStatus.BAD_REQUEST, "/error/400");
        ErrorPage errorPage401 = new ErrorPage(HttpStatus.UNAUTHORIZED, "/error/401");
        ErrorPage errorPage403 = new ErrorPage(HttpStatus.FORBIDDEN, "/error/403");
        ErrorPage errorPage404 = new ErrorPage(HttpStatus.NOT_FOUND, "/error/404");
        ErrorPage errorPage415 = new ErrorPage(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "/error/415");
        ErrorPage errorPage500 = new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500");
        factory.addErrorPages(errorPage400, errorPage401, errorPage403, errorPage404, errorPage415, errorPage500);
        return factory;
    }
}

最后,需要讲解配置类:SecurityConfig

package com.study.config;

import com.study.handler.MyAuthenticationFailureHandler;
import com.study.handler.MyAuthenticationSuccessHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.DelegatingPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/**
 * @author biandan
 * @description
 * @signature 让天下没有难写的代码
 * @create 2021-05-30 下午 9:38
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyAuthenticationSuccessHandler successHandler;

    @Autowired
    private MyAuthenticationFailureHandler failureHandler;

    // 配置认证用户信息和权限
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 添加admin账号,拥有增删改查权限
        auth.inMemoryAuthentication().withUser("admin").password("123").
                authorities("showUser","addUser","updateUser","deleteUser");
        // 添加user账号,只有查询权限
        auth.inMemoryAuthentication().withUser("user").password("456")
                .authorities("showUser");

    }

    // 配置拦截请求资源
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 如何权限控制 给每一个请求路径 分配一个权限名称 然后账号只要关联该名称,就可以有访问权限
        http.authorizeRequests()
                // 配置查询用户权限
                .antMatchers("/showUser").hasAnyAuthority("showUser")
                .antMatchers("/addUser").hasAnyAuthority("addUser")
                .antMatchers("/updateUser").hasAnyAuthority("updateUser")
                .antMatchers("/deleteUser").hasAnyAuthority("deleteUser")
                .antMatchers("/login").permitAll()
                .antMatchers("/**").fullyAuthenticated().and()
                .formLogin()
                .loginPage("/login")
                .successHandler(successHandler).failureHandler(failureHandler)
                .and().csrf().disable();
    }

    /* 不推荐使用这种方式,查看博客:https://blog.csdn.net/alinyua/article/details/80219500
    @Bean
    public static NoOpPasswordEncoder passwordEncoder() {
        return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
    }
    */

    @Bean
    public static PasswordEncoder passwordEncoder(){
        DelegatingPasswordEncoder encoder = (DelegatingPasswordEncoder)PasswordEncoderFactories.createDelegatingPasswordEncoder();
        encoder.setDefaultPasswordEncoderForMatches(NoOpPasswordEncoder.getInstance());
        return encoder;
    }

}

需要注意的是我们需要增加 PasswordEncoder 的 Bean 注解,如果不增加会报错:

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

原因是升级为 Security5.0 以上密码支持多种加密方式,我们需要恢复以前的模式,不推荐使用 NoOpPasswordEncoder 而是使用 PasswordEncoder。

我们还要禁用 csrf ,同时因为我们使用的是 FromLogin 的方式,如果不禁掉 csrf,必须在表单里传递 token 才能通过。我们设置登录不拦截,代码如下:

.antMatchers("/login").permitAll()

然后,我们需要增加认证成功处理类:

package com.study.handler;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author biandan
 * @description 认证成功
 * @signature 让天下没有难写的代码
 * @create 2021-05-30 下午 9:43
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        String username = httpServletRequest.getParameter("username");
        System.out.println("认证成功,username="+username);
        //认证成功后跳转到主页
        httpServletResponse.sendRedirect("/");
    }
}

认证失败处理类: 

package com.study.handler;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author biandan
 * @description 认证失败
 * @signature 让天下没有难写的代码
 * @create 2021-05-30 下午 9:45
 */
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        String username = httpServletRequest.getParameter("username");
        System.out.println("认证失败,username="+username);
        //重定向到登录页面,同时在地址后面增加 error 参数
        httpServletResponse.sendRedirect("/login?error");
    }
}

application.yml 配置:

server:
  port: 80

# 将SpringBoot项目作为单实例部署调试时,不需要注册到注册中心
eureka:
  client:
    fetch-registry: false
    register-with-eureka: false

spring:
  application:
    name: security-server
  # 配置freemarker
  freemarker:
    # 设置模板后缀名
    suffix: .ftl
    # 设置文档类型
    content-type: text/html
    # 设置页面编码格式
    charset: UTF-8
    # 设置页面缓存
    cache: false
    # 设置ftl文件路径
    template-loader-path:
      - classpath:/templates
  # 设置静态文件路径,js,css等
  mvc:
    static-path-pattern: /static/**

OK,启动我们的微服务,浏览器地址输入:http://127.0.0.1/

输入 admin 账号,123密码,登录成功:

admin 有增删改查的权限,因此点击每个按钮都有权限。

然后我们切换到 user 账号,user 账号只有查询的权限,点击增删改提示权限不足。

同时我们查看后台日志:

OK,security 的核心流程我们讲解完毕,后面会讲解如何动态的分配权限。这篇博客仅仅做入门,实际开发不可能写固定用户名和密码、权限到代码里去的。

 

本篇博客代码:https://pan.baidu.com/s/1R0Daoh7Zdw1-DQKrLYAbkw   提取码:pshm

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
Spring Security是基于Spring框架的一套完整的Web应用安全解决方案。它提供了用户认证和用户授权的功能,通过配置Spring Security,可以实现对Web应用的访问控制和安全保护。 相对于其他安全管理技术栈(如Shiro),在SSM中整合Spring Security可能会更加繁琐。但是,自从有了Spring Boot之后,Spring Boot为Spring Security提供了自动化配置方案,可以减少配置的数量。因此,现在常见的安全管理技术栈组合中,通常会使用Spring Boot和Spring Security。 如果你希望防止未经授权的用户访问某个页面,比如"/hello",你可以通过在应用程序中配置Spring Security来实现。当Spring Security存在于类路径上时,Spring Boot会自动使用"Basic认证"来保护所有HTTP端点。同时,你也可以进一步自定义安全设置。 为了使用Spring Security,你需要在pom文件中引入相应的依赖,比如"spring-boot-starter-security"。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [spring security原理和机制 | Spring Boot 35](https://blog.csdn.net/guorui_java/article/details/118229097)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [spring security——基本介绍(一)](https://blog.csdn.net/qq_22172133/article/details/86503223)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值