SpringSecurity的简单使用

该文章详细介绍了如何在项目中集成SpringSecurity,包括引入相关依赖,使用MybatisPageHelperPro生成实体类和接口,实体类实现UserDetails接口,创建自定义Security配置类,以及定制访问拒绝处理器、认证成功和失败处理器等。整个过程覆盖了用户认证、权限管理及异常处理的关键步骤。

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

1、导入依赖

 <dependencies>
 		//导入SpringSecurity
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        //导入SpringBoo-tWeb
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        //导入mysql
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.21</version>
        </dependency>
        //整合mybatis-spring
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2、使用MybatisPageHelperPro逆向生成实体类和接口、接口mapper

3、实体类实现UserDetails接口重写里面的方法

@Data
public class UserBean implements UserDetails {
    /**
    * 编号
    */
    private Integer userId;
        /**
    * 登陆名
    */
    private String username;
    ...........
    private List<SimpleGrantedAuthority> permission=new ArrayList<>();
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return permission;
    }
        /**
     * 密码是否过期
     * @return
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNoExpired.equals(1);
    }

    /**
     * 账户是否被禁用
     * @return
     */
    @Override
    public boolean isEnabled() {
        return enabled.equals(1);
    }
}
/*
 * 自定义前端请求统一响应格式
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResponseData {
    private Integer code=200;
    private String msg="响应成功";
    private Object result;

    public ResponseData(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public ResponseData(Object result) {
        this.result = result;
    }
}
/**
 * 
 */
public class ResponseJsonUtil {

    /**
     * 统一在响应流中输入json字符串
     * @param response
     * @param data
     */
    public static  void  response(HttpServletResponse response, ResponseData data){
        ObjectMapper mapper=new ObjectMapper();
        try {
            String jsonStr=mapper.writeValueAsString(data);

            response.setCharacterEncoding("UTF-8");
            response.setContentType("text/json;charset=utf-8");
            PrintWriter writer = response.getWriter();
            writer.write(jsonStr);
            writer.flush();
            writer.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

4、创建ServiceImpl类实现UserDetailsService接口重写loadUserByUsername方法

@Service
public class UserServiceImpl implements UserService, UserDetailsService {
    @Autowired
    private UserBeanMapper userBeanMapper;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //根据用户名从数据库获取密码
        UserBean user = userBeanMapper.selectByUserName(username);
        if(null==user){
            throw new UsernameNotFoundException("用户名不存在");
        }
        //根据用户id从数据库获取用户的权限
        List<String> pers = userBeanMapper.selectPermissionCodeByUserId(user.getUserId());
        //将获取到的用户权限一个个放进List<SimpleGrantedAuthority>中
        for(String per : pers){
            user.getPermission().add(new SimpleGrantedAuthority(per));
        }
        return user;
    }
}    

5、创建自定义Security配置类继承WebSecurityConfigurerAdapter抽象类


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启方法级别的权限认证  默认是false
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserServiceImpl userService;
    /**
     * 配置用户信息,模拟内存用户数据
     *
     * @param auth
     * @throws Exception
     */
   @Override
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
       //根据传入的自定义 UserDetailsService 添加身份验证。
       // 然后它返回一个DaoAuthenticationConfigurer对象以允许自定义身份验证
       auth.userDetailsService(userService);
   }


    @Autowired//注入身份认证失败处理器对象
    private AppAuthFailHandler appAuthFailHandler;
    @Autowired//注入身份认证成功处理器对象
    private AppAuthSuccessHandler appAuthSuccessHandler;
    @Autowired//注入访问拒绝处理器对象
    private AppAuthDenyHander appAuthDenyHander;
    @Autowired//注入退出登陆处理器对象
    private AppLogoutHandler appLogoutHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //身份验证
        http.formLogin() //返回一个基于表单的身份验证对象
               .successHandler(appAuthSuccessHandler)//登陆成功后执行的处理器
               .failureHandler(appAuthFailHandler);//登陆失败后执行的处理器

        //无权访问
        http.exceptionHandling().accessDeniedHandler(appAuthDenyHander);

        //退出登陆处理器
        http.logout().addLogoutHandler(appLogoutHandler);
        //配置路径拦截 的url的匹配规则
        http.authorizeRequests()
                //放行不需要权限的页面
                .mvcMatchers("/user/u1").anonymous()
                //任何路径要求必须认证之后才能访问
                .anyRequest().authenticated();

    }

    /*
     * 从 Spring5 开始,强制要求密码要加密
     * 如果非不想加密,可以使用一个过期的 PasswordEncoder 的实例 NoOpPasswordEncoder,
     * 但是不建议这么做,毕竟不安全。
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

6、自定义访问拒绝处理器,认证成功处理器、认证失败处理器,登出处理器,需要注入自定义Security配置类,交给Security处理

/*
 * 自定义访问拒绝处理器,实现AccessDeniedHandler接口
 */
@Component
public class AppAuthDenyHander implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        ResponseJsonUtil.response