您好,欢迎访问代理记账网站
  • 价格透明
  • 信息保密
  • 进度掌控
  • 售后无忧

从零搭建若依(Ruoyi-Vue)管理系统(11)--整合Spring Security

文章目录

    • 1.添加依赖
    • 2. 添加用户信息
    • 3. 配置Spring Security
    • 4. 需要添加的功能类
    • 5. 配置代码
    • 6. 测试一些功能

本章先按照若依中源码实现的配置和功能进行搭建,后续会有一些改变
本章结束后对应的节选代码文件:Gangbb-Vue-11-SpringSecurity
https://github.com/Gang-bb/Study-Record/tree/main/Gangbb-Vue
历史遗留TODO:

  • 第四章
  1. 登录日志还未实现。(到登录和权限模块完成)
  2. LogAspect从缓存获取当前的用户信息使用模拟的数据(到登录和权限模块完成)

本章将留下TODO:

  • 第十一章
  1. 需要在Spring Security中注册的登录鉴权逻辑处理类、JWT处理的过滤器、退出登录处理类(到登录和权限模块完成)

本章将解决TODO:

1.添加依赖

Gangbb-commonpom.xml

<dependencies>
	<!-- spring security 安全认证 begin-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <!-- spring security 安全认证 end-->
</dependencies>

2. 添加用户信息

首先要认证就必要要有用户。所以我们新建一个用户表sys_user

CREATE TABLE `sys_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
  `user_name` varchar(30) NOT NULL COMMENT '用户账号',
  `nick_name` varchar(30) NOT NULL COMMENT '用户昵称',
  `user_type` varchar(2) DEFAULT '00' COMMENT '用户类型(00系统用户)',
  `email` varchar(50) DEFAULT '' COMMENT '用户邮箱',
  `phone_number` varchar(11) DEFAULT '' COMMENT '手机号码',
  `sex` tinyint(4) unsigned DEFAULT '0' COMMENT '用户性别(0男 1女 2未知)',
  `avatar` varchar(150) DEFAULT '' COMMENT '头像地址',
  `password` varchar(100) DEFAULT '' COMMENT '密码',
  `status` tinyint(4) unsigned DEFAULT '0' COMMENT '用户状态 1-启用 2-禁用 3-锁定',
  `del_flag` tinyint(4) unsigned DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `login_ip` varchar(50) DEFAULT '' COMMENT '最后登录IP',
  `login_date` datetime DEFAULT NULL COMMENT '最后登录时间',
  `error_count` tinyint(4) unsigned DEFAULT '0' COMMENT '错误次数',
  `creator` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `reviser` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COMMENT='用户信息表';

并在项目中新建对应的SysUserSysUserServiceSysUserMapperSysUserServiceImplSysUserMapper.xml

3. 配置Spring Security

所有配置都在package com.gangbb.core.config.SecurityConfig中。沿用若依的配置(若依中配置文件在package com.ruoyi.framework.config.SecurityConfig)。

关于@EnableGlobalMethodSecurity可以查看上节。

补充1:

Spring Security 中的退出登录

内置 Filter 全解析 我们知道退出登录逻辑是由过滤器 LogoutFilter 来执行的。 它持有三个接口类型的属性:

  1. RequestMatcher: logoutRequestMatcher 这个用来拦截退出请求的 URL
  2. LogoutHandler: handler 用来处理退出的具体逻辑 (若依中只用了这个配置)
  3. LogoutSuccessHandler: logoutSuccessHandler 退出成功后执行的逻辑

我们通过对以上三个接口的实现就能实现我们自定义的退出逻辑.

补充2:

Spring Security 中的异常

Spring Security 中的异常主要分为两大类:一类是认证异常,另一类是授权相关的异常。

AuthenticationException 是在用户认证的时候出现错误时抛出的异常。

Spring Security 中的异常处理

HttpSecurity提供的exceptionHandling()方法用来提供异常处理。该方法构造出 ExceptionHandlingConfigurer 异常处理配置类。该配置类提供了两个实用接口: AuthenticationEntryPoint 该类用来统一处理 AuthenticationException 异常。 AccessDeniedHandler 该类用来统一处理 AccessDeniedException 异常。

我们只要实现并配置这两个异常处理类即可实现对 Spring Security 认证授权相关的异常进行统一的自定 义处理。

4. 需要添加的功能类

  • 一个继承于AuthenticationEntryPointAuthenticationEntryPointImpl 认证失败处理类。处理类核心就是让Spring Security 验证失败返回一个自定义的ApiRestResponse
package com.ruoyi.framework.security.handle;

import java.io.IOException;
import java.io.Serializable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import com.alibaba.fastjson.JSON;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;

/**
 * 认证失败处理类 返回未授权
 * 
 * @author ruoyi
 */
@Component
public class AuthenticationEntryPointImpl implements AuthenticationEntryPoint, Serializable
{
    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e)
            throws IOException
    {
        int code = HttpStatus.UNAUTHORIZED;
        String msg = StringUtils.format("请求访问:{},认证失败,无法访问系统资源", request.getRequestURI());
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(code, msg)));
    }
}

以下是与登录登出相关的类

(登录鉴权下章开讲)

登录鉴权逻辑处理类:

  • 继承UserDetailsServiceUserDetailsServiceImpl

JWT处理的过滤器:

  • JwtAuthenticationTokenFiltertoken认证过滤器

配合LogoutHandler配置的:

  • LogoutSuccessHandlerImpl退出登录处理类

5. 配置代码

本章结束后的SecurityConfig

package com.gangbb.core.config;


import com.gangbb.core.security.filter.LangFilter;
import com.gangbb.core.security.handle.AuthenticationEntryPointImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

/**
 * @author : Gangbb
 * @ClassName : SecurityConfig
 * @Description : spring security配置
 * @Date : 2021/3/13 10:52
 */


@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
    @Autowired
    private LangFilter langFilter;

    @Autowired
    private AuthenticationEntryPointImpl authenticationEntryPoint;


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

    /** antMatchers()过滤请求中:
     *
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问(非remember-me下自动登录)
     * hasAnyAuthority     |   如果有参数,参数表示权限,则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数,参数表示角色,则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数,参数表示权限,则其权限可以访问
     * hasIpAddress        |   如果有参数,参数表示IP地址,如果用户IP和参数匹配,则可以访问
     * hasRole             |   如果有参数,参数表示角色,则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception
    {
        httpSecurity
                // CSRF禁用,因为不使用session
                .csrf().disable()
                // 认证失败处理类
                .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint).and()
                // 基于token,所以不需要session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
                // 过滤请求
                .authorizeRequests()
                // 对于登录login 验证码captchaImage 允许匿名访问
                .antMatchers("/login", "/captchaImage").anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/*.html",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js"
                ).permitAll()
                .antMatchers("/profile/**").anonymous()
                .antMatchers("/common/download**").anonymous()
                .antMatchers("/common/download/resource**").anonymous()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/**").anonymous()
                .antMatchers("/webjars/**").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                .antMatchers("/test/**").anonymous()
                // 除上面外的所有请求全部需要鉴权认证
                .anyRequest().authenticated()
                .and()
                // 允许使用内嵌网页iframe(springSecurity 默认使用X-Frame-Options防止网页被Frame)
                .headers().frameOptions().disable();
//                .and()
//                .addFilterBefore(langFilter, BasicAuthenticationFilter.class);
        //httpSecurity.addFilter(langFilter);
        //httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
        // 添加JWT filter
        //httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
        // 添加CORS filter
        //httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);
        //httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class);
    }


    /**
     * 强散列哈希加密实现
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder()
    {
        return new BCryptPasswordEncoder();
    }

    /**
     * 身份认证接口
     */
//    @Override
//    protected void configure(AuthenticationManagerBuilder auth) throws Exception
//    {
//        auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
//    }
}

6. 测试一些功能

关闭对测试接口的放行:

image-20210430095810401

请求结果:

image-20210430095905094

开启对测试接口的放行后:

image-20210430095700218

分享:

低价透明

统一报价,无隐形消费

金牌服务

一对一专属顾问7*24小时金牌服务

信息保密

个人信息安全有保障

售后无忧

服务出问题客服经理全程跟进