SpringSecurity安全框架的配置类+处理器+service

本文介绍了如何在SpringBoot应用中使用Spring Security进行权限管理,包括配置类的设置,自定义登录成功和失败处理器,以及异常访问处理器。通过WebSecurityConfigurerAdapter配置HTTP安全,实现所有请求必须认证后才能访问,并利用BCryptPasswordEncoder进行密码加密。同时,文章展示了如何处理AccessDeniedException,返回JSON格式的错误信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

适用于前后端分离的SpringSecurity框架

配置类:

package com.example.demo3.config;

import com.example.demo3.config.Handler.RestAuthorizationAccessDeniedHandler;
import com.example.demo3.service.impl.MyUserDetailsServiceImpl;
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.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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;


/**
 * @author 无敌暴龙神
 * 注解表示开启安全框架,开始过滤所有的url;同时标志这个类是一个配置类
 */
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 自己定义的处理器;;;可能是错的,因为我忘了这个是干啥的
     */
    private RestAuthorizationAccessDeniedHandler accessDeniedHandler;

    /**
     * 将自定义的登录成功处理器注入进来
     */
    @Autowired
    private AuthenticationSuccessHandler authenticationSuccessHandler;

    /**
     * 将自定义的登录失败处理器注入进来
     */
    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    /**
     * 配置http请求验证等
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 给一个异常处理器,走我们自定义的拒绝访问处理器
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
        //给定处理器;登录成功的处理器和登录失败的处理器
        http.formLogin().
                successHandler(authenticationSuccessHandler).
                failureHandler(authenticationFailureHandler);
        // 所有的请求都需要登录才能进行
        http.authorizeRequests()
                .anyRequest().authenticated();
    }

    /**
     * 创建密码加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

异常处理器:

package com.example.demo3.config.Handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import jdk.nashorn.internal.ir.Assignment;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @author 无敌暴龙神
 * 处理器
 */
@Configuration
public class RestAuthorizationAccessDeniedHandler implements AccessDeniedHandler {
    /**
     *
     * @param request
     * @param response
     * @param e
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
        // 设置数据类型
        response.setContentType("application/json;charset=utf-8");
        // 封装数据
        Map<String, Object> data = new HashMap<>(4);
        //
        data.put("code", "403");
        data.put("msg", "您没有访问权限");
        // 拿到响应流
        PrintWriter writer = response.getWriter();
        // 创建类型转换器
        ObjectMapper objectMapper = new ObjectMapper();
        String s = objectMapper.writeValueAsString(data);
        // 将数据写出去
        writer.write(s);
        writer.flush();
        writer.close();
    }
}

登录成功的处理器

package com.example.demo3.config.Handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * 登录成功的处理器
 */
@Configuration
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        //设置返回数据格式
        response.setContentType("application/json;charset=utf-8");
        //获取输出流
        PrintWriter pw = response.getWriter();
        //定义集合
        Map<String, Object> map = new HashMap<>();
        //给集合添加参数
        map.put("code", 200);
        // 将用户信息放进去
        map.put("msg", authentication.getPrincipal());
        //把map写到前端
        pw.write(new ObjectMapper().writeValueAsString(map));
        pw.flush();
        pw.close();
    }
}

登录失败的处理器

package com.example.demo3.config.Handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        //设置返回的数据格式
        response.setContentType("application/json;charset=utf-8");
        //获取数据流
        PrintWriter pw = response.getWriter();
        Map<String, Object> map = new HashMap<>();
        map.put("code", 401);
        //根据异常返回不同的msg
        if (exception instanceof LockedException) {
            map.put("msg", "账户被锁定,登陆失败!");
        } else if (exception instanceof BadCredentialsException) {
            map.put("msg", "账户或者密码错误,登陆失败!");
        } else if (exception instanceof DisabledException) {
            map.put("msg", "账户被禁用,登陆失败!");
        } else if (exception instanceof AccountExpiredException) {
            map.put("msg", "账户已过期,登陆失败!");
        } else if (exception instanceof CredentialsExpiredException) {
            map.put("msg", "密码已过期,登陆失败!");
        } else {
            map.put("msg", "登陆失败!");
        }
        pw.write(new ObjectMapper().writeValueAsString(map));
        pw.flush();
        pw.close();
    }
}

service层

package com.example.demo3.service.impl;

import com.example.demo3.mapper.SysUserMapper;
import com.example.demo3.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.List;

/**
 * @author 无敌暴龙神
 * 实现UserDetailsService接口
 */
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {


    /**
     * 注入mapper层的接口,在数据库中去查询对应的用户信息
     */
    @Autowired
    private SysUserMapper sysUserMapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        //判断用户名是否为空
        if (s==null){
            return null;
        }
        //根据用户名去sql表中查询用户
        SysUser sysUser=sysUserMapper.findUserByUserName(s);
        //判断是否存在该用户
        if (sysUser==null){
            return null;
        }
        //根据用户的id去查询对应的权限合集
        List<String> auths=sysUserMapper.findAuthByUserId(sysUser.getUserid());
        //将查询得到的权限合集注入到实体类
        sysUser.setAuths(auths);
        //返回实体类
        return sysUser;
    }
}

实体类

package com.example.demo3.pojo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * @author 无敌暴龙神
 * 实现UserDetails接口,这样service层重写的方法可以直接返回该实体类
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysUser implements Serializable, UserDetails {
    private Integer userid;

    private String username;

    private String userpwd;

    private String sex;

    private String address;

    /**
    * 用户状态字段(1:正常 0:禁用)
    */
    private Integer status;

    private static final long serialVersionUID = 1L;

    /**
     * 权限合集,由外部service层注入
     */
    private List<String> auths=null;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //创建GrantedAuthority的list集合
        List<GrantedAuthority> list=new ArrayList<>();
        //循环查询得到的权限合集,将权限合集存入该list结合
        this.auths.forEach(auth->{
            list.add(new SimpleGrantedAuthority(auth));
        });
        return list;
    }

    /**
     *
     * @return 返回查询的用户名;这个方法与本实体类的get属性的方法方法名一致,会被idea默认覆盖
     * 需要手动添加该getUsername放
     */
    @Override
    public String getUsername() {
        return this.username;
    }

    /**
     *
     * @return 返回用用户名查询到的密码,返回
     */
    @Override
    public String getPassword() {
        return this.userpwd;
    }

    /**
     * 判断用户状态====过期
     * @return
     */
    @Override
    public boolean isAccountNonExpired() {
        return this.status==1;
    }

    /**
     * 判断用户状态====锁定
     * @return
     */
    @Override
    public boolean isAccountNonLocked() {
        return this.status==1;
    }

    /**
     * 判断用户状态====凭证过期
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return this.status==1;
    }

    /**
     * 判断用户状态====禁用
     * @return
     */
    @Override
    public boolean isEnabled() {
        return this.status==1;
    }
}

测试后发现,当我service层实现了

UserDetailsService接口后,重写了loadUserByUsername方法;

框架会默认直接去访问自己写的servic层去获取用户住处;而不是去内存中获取用户住处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值