Spring securty<三> 认证案例代码

本文档详细介绍了SpringSecurity的认证流程,包括配置登录、异常处理和相关处理器的实现。通过构建项目,展示了如何配置WebSecurity,设置登录成功和失败处理器,以及全局的访问拒绝和未认证异常处理器。同时,提供了Swagger UI的登录接口测试,验证了登录认证功能的正确性。
摘要由CSDN通过智能技术生成

Spring securty<三> 认证案例代码


本地项目的基础环境

环境版本
jdk1.8.0_201
maven3.6.0
Spring-boot2.3.3.RELEASE

1、简介

spring security是一个提供身份验证、授权和防止常见攻击的框架,它对命令式和反应式应用程序都有一流的支持,是保护基于Spring的应用程序的事实上的标准。

详细可以参看《spring security官网》

2、认证(登录)

通过之前的两篇文章的介绍,应该也比较清楚了基本的概念了安全框架里的核心的概念了,从这篇开始,主要开始细化讲代码层面上的开发了;在权限框架中,认证这个部分,也算是最难的了,之后的几篇,也是主要讲述认证相关的。

《Spring securty<一> 简介入门案例》

《Spring securty<二> 配置项详解》

3、构建项目

新建项目badger-spring-securty-3

3.1、pom文件

为了方便,在pom文件中,我加入了swagger-ui的相关组件,以及json相关的包

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
    xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath />
    </parent>
    <groupId>com.badger</groupId>
    <artifactId>badger-spring-security-3</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>badger-spring-security-3</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
        <swagger.version>2.8.0</swagger.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.78</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- 用于自动生成 API 文档 -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${swagger.version}</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${swagger.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2、springboot 主启动类以及演示的controller接口

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecurityApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SecurityApplication.class, args);
    }
}

3.3、swagger-ui的配置类

/**
 * 基于Swagger生成API文档
 * @author: liqi
 */
@Configuration
@EnableSwagger2
public class SwaggerConfiguration {

    static final String TEST_TOKEN_101 = "";

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.basePackage("com.badger.spring.boot.security")).paths(PathSelectors.any())
                .build().globalOperationParameters(parameters());
    }

    private List<Parameter> parameters() {
        ParameterBuilder ticketPar = new ParameterBuilder();
        ticketPar.name("Authorization");
        ticketPar.description("验证的token,测试使用");
        ticketPar.modelRef(new ModelRef("string"));
        ticketPar.defaultValue(TEST_TOKEN_101);
        ticketPar.parameterType("header");
        ticketPar.required(false);
        return Arrays.asList(ticketPar.build());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title("安全框架").description("内部接口").version("1.0.0-RELEASE").build();
    }
}

3.4、统一返回的处理类

@Data
public class Result {

    private Integer code;

    private String message;

    private Result(Integer code, String messgae) {
        this.code = code;
        this.message = messgae;
    }

    public static Result success(String message) {
        return new Result(200, message);
    }

    public static Result result(Integer code, String messgae) {
        return new Result(code, messgae);
    }
}

3.5、WebSecurity 配置类

package com.badger.spring.boot.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private static final String[] EXCLUDE_URLS = { "/**/*.js", "/**/*.css", "/**/*.jpg", "/**/*.png", "/**/*.gif",
            "/v2/**", "/errors", "/error", "/favicon.ico", "/swagger-ui.html/**", "/swagger-ui/**", "/webjars/**",
            "/swagger-resources/**", "/auth/login" };
    @Autowired
    private AuthenticationSuccessHandler successHandler;
    @Autowired
    private AuthenticationFailureHandler failureHandler;
    @Autowired
    AccessDeniedHandler deniedHandler;
    @Autowired
    AuthenticationEntryPoint entryPoint;

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("test").password(passwordEncoder().encode("123456"))
                .authorities("admin");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 全局异常配置
        http.exceptionHandling().accessDeniedHandler(deniedHandler).authenticationEntryPoint(entryPoint);
        http.authorizeRequests().antMatchers(EXCLUDE_URLS).permitAll();
        // 1、表单操作
        FormLoginConfigurer<HttpSecurity> formLogin = http.formLogin();
        // 表单请求成功处理器、失败处理器;与loginPage冲突,配置后,loginPage不生效
        formLogin.successHandler(successHandler).failureHandler(failureHandler);
        // 表单提交的post请求地址,用户参数名称
        formLogin.loginProcessingUrl("/auth/login");
        http.csrf().disable();
    }
}

1、@EnableWebSecurity 注解,开启Web的Security的注解

2、配置一个默认用户,帐号为test,密码为123456

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("test").password(passwordEncoder().encode("123456"))
                .authorities("admin");
    }

3、基础帐号密码登录配置

    @Autowired
    private AuthenticationSuccessHandler successHandler;
    @Autowired
    private AuthenticationFailureHandler failureHandler;
	
    @Override
    protected void configure(HttpSecurity http) throws Exception {
         // 全局异常配置
        http.exceptionHandling().accessDeniedHandler(deniedHandler).authenticationEntryPoint(entryPoint);
        http.authorizeRequests().antMatchers(EXCLUDE_URLS).permitAll();
        // 1、表单操作
        FormLoginConfigurer<HttpSecurity> formLogin = http.formLogin();
        // 表单请求成功处理器、失败处理器;与loginPage冲突,配置后,loginPage不生效
        formLogin.successHandler(successHandler).failureHandler(failureHandler);
        // 表单提交的post请求地址,用户参数名称
        formLogin.loginProcessingUrl("/auth/login");
        http.csrf().disable();
    }

全局的异常处理器

排除一些css、js、swagger-ui的接口、样式等

这里需要登录成功的处理器,以及登录失败的处理器,下一个小节贴基础代码;

3.6、拒绝访问处理器

/**
 * 全局异常处理器
 * @author liqi
 */
@Component
public class DeniedHandler implements AccessDeniedHandler {

    @SuppressWarnings("deprecation")
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
            AccessDeniedException accessDeniedException) throws IOException, ServletException {
        String error = "请求Url:" + request.getRequestURI() + " 鉴权失败:" + accessDeniedException.getMessage();
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(HttpStatus.UNAUTHORIZED.value(), error)));
        writer.flush();
        writer.close();
    }
}

3.7、未认证的全局处理器

/**
 * 未认证的全局处理器
 * 没有认证时,在这里处理结果,不要重定向
 * @author liqi
 *
 */
@Component
public class EntryPoint implements AuthenticationEntryPoint {

    @SuppressWarnings("deprecation")
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        String error = "请求Url:" + request.getRequestURI() + " 认证失败:" + authException.getMessage();
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(HttpStatus.UNAUTHORIZED.value(), error)));
        writer.flush();
        writer.close();
    }
}

3.8、登录成功的处理器

/**
 * 登录成功,事件处理器
 * @author liqi
 */
@Component
@Slf4j
public class SuccessHandler implements AuthenticationSuccessHandler {

    @SuppressWarnings("deprecation")
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) throws IOException, ServletException {
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        log.info("登录成功:{}", authentication.getPrincipal());
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.success(authentication.getPrincipal().toString())));
        writer.flush();
        writer.close();
    }
}

3.9、认证失败的处理器

/**
 * 登录失败处理器
 * @author liqi
 */
@Component
public class FailureHandler implements AuthenticationFailureHandler {

    private Logger log = LoggerFactory.getLogger(getClass());

    @SuppressWarnings("deprecation")
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败,{}", exception.getMessage());
        response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
        PrintWriter writer = response.getWriter();
        writer.print(JSON.toJSONString(Result.result(400, exception.getMessage())));
        writer.flush();
        writer.close();
    }
}

3.10、最后一个swagger-ui的登录接口测试类

@RestController
@Api(tags = { "登录接口" })
public class LoginController {

    @ApiOperation(value = "账号密码登陆")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "username", value = "用户名", dataType = "String", paramType = "query", defaultValue = "test"),
            @ApiImplicitParam(name = "password", value = "密码", dataType = "String", paramType = "query", defaultValue = "123456") })
    @PostMapping("/auth/login")
    public void login(@RequestParam(name = "username", required = true) String username,
            @RequestParam(name = "password", required = true) String password) {
    }
}

可以看到,这个登录接口,里面啥的都没有,只是定义了参数,并没有返回,实际业务,也没有走这个接口,而是通过Security的拦截器链,来处理的,定义这个接口,也只是为了,swagger-ui可以扫描到,好调用,如果使用postMain调用,这个接口,可以完全不用写;

设置的默认密码,也跟上述一致,user/123456

4、测试验证

启动主启动类,打开swagger-ui页面

http://localhost:8080/swagger-ui.html#/登录接口

登录正确的帐号密码

{
  "code": 200,
  "message": "test"
}

错误的密码

{
  "code": 400,
  "message": "Bad credentials"
}

可以看到,测试成功了;

详细的代码,可以查看《码云》

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

葵花下的獾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值