基础权限框架搭建实现

本文涉及的产品
对象存储 OSS,20GB 3个月
对象存储 OSS,内容安全 1000次 1年
对象存储 OSS,恶意文件检测 1000次 1年
简介: 基础权限框架搭建实现
后台权限框架搭建:本项目权限主要依赖 SpringSecurity实现,主要涉及的表有 角色表菜单表以及 角色菜单关联表等数据库表。权限部分功能的实现需要使用到自定义配制文件、自定义注解、自定义服务类等等...

1、项目整合SpringSecurity

1.1、引入SpringSecurity依赖

        <!--===================== SpringBoot相关依赖版本  =========================-->
        <springboot.version>2.5.5</springboot.version>
            <!-- security -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
                <version>${springboot.version}</version>
            </dependency>

1.2、启动测试

启动项目之后访问接口发现已经被SpringSecurity拦截,出现如下界面说明SpringSecurity已经成功引入

在这里插入图片描述

1.3、自定义实体类继承UserDetails

由于SpringSecurity默认提供的登陆接口会执行loadUserByUsername()方法,此方法的返回值为UserDetails,而SpringSecurity会根据返回值中的加密密码进行密码校验,所以我们需要自定义一个实体类来继承UserDetails
/**
 * @author 木字楠
 * @version 1.0
 * @description 用户信息
 * @date 2022/8/12
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User implements UserDetails {

    /**
     * 用户主键
     */
    private String id;

    /**
     * 用户名/OpenId
     */
    private String username;

    /**
     * 用户密码
     */
    private String password;

    /**
     * 用户登录方式(1 用户名  2 邮箱  3 QQ  4 微信)
     */
    private Integer loginType;

    /**
     * 是否开启邮箱登录(0 否  1是)
     */
    private Boolean emailLogin;

    /**
     * 是否禁用(0 正常  1禁用)
     */
    private Boolean disabled;

    /**
     * 用户名
     */
    private String nickname;

    /**
     * 用户头像
     */
    private String avatar;

    /**
     * 用户性别(-1 未知  0 仙女  1帅哥)
     */
    private String gender;

    /**
     * 用户邮箱
     */
    private String email;

    /**
     * 用户个人简介
     */
    private String personIntro;

    /**
     * ip地址
     */
    private String ipAddress;

    /**
     * ip来源
     */
    private String ipSource;

    /**
     * 用户创建时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    private LocalDateTime gmtCreate;

    /**
     * 用户信息更新时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    private LocalDateTime gmtUpdate;

    /**
     * 用户最近一次登录时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd hh:mm:ss")
    private LocalDateTime lastLoginTime;

    /**
     * 浏览器
     */
    private String brower;

    /**
     * 操作系统
     */
    private String os;

    /**
     * 角色
     */
    private String role;

    /**
     * 权限列表
     */
    private Set<String> permissionList;

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        SimpleGrantedAuthority list = new SimpleGrantedAuthority(role);
        return Collections.singleton(list);
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

1.4、自定义配制文件

引入依赖
        <!-- 自定义配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
自定义配制信息
# custom Configuration
application:
  #Swagger Configuration
  swagger:
    isEnable: true
  #Super_admin Configuration
  highest-authority:
    highest-role-label: super_admin
    highest-role-secret: MuZiNan#$%^&*
    highest-permission: <*><*><*>
我们可以使用@Value的注解进行自定义配制信息的提取,但是那样做法太Low,而且不方便。这里我们采用更加优雅的方式来提起自定义注解信息!使用Properties配制文件来提取自定义配制信息!
/**
 * @author 木字楠
 * @version 1.0
 * @description 最高权限配制文件
 * @date 2022/8/12
 */
@Data
@ConfigurationProperties(HIGHEST_AUTHORITY)
public class HigestAuthorityProperties {

    /**
     * 最高权限角色标识
     */
    private String highestRoleLabel;

    /**
     * 最高权限角色秘钥
     */
    private String highestRoleSecret;

    /**
     * 最高权限标识
     */
    private String highestPermission;
}
我们是用常量的形式来简化字符串的拼接
# ConfigPropertiesConstant

/**
 * @author: 木字楠
 * @date: 2022/8/12
 * @version: 1.0
 */
public class ConfigPropertiesConstant {

    /**
     * 应用相关前缀,位于“Application”配置中
     */
    public static class Application {

        /**
         * 默认前缀
         */
        private static final String DEFAULT_PREFIX = "application";

        /**
         * JWT相关配置信息
         */
        public static final String JWT = DEFAULT_PREFIX + ".jwt";

        /**
         * 最高权限配制信息
         */
        public static final String HIGHEST_AUTHORITY = DEFAULT_PREFIX + ".highest-authority";

        /**
         * 阿里云Oss对象存储
         */
        public static final String ALIYUN_OSS = DEFAULT_PREFIX + ".aliyun.oss";

        /**
         * 七牛云Kodo对象存储
         */
        public static final String QINIU_KODO = DEFAULT_PREFIX + ".qiniu.kodo";

        /**
         * 服务器Ftp上传
         */
        public static final String ECS_FTP = DEFAULT_PREFIX + ".ecs.ftp";

    }

}
开启配制文件的使用
/**
 * @author 木字楠
 * @version 1.0
 * @description 配制文件是否开始使用
 * @date 2022/8/12
 */
@Configuration
@EnableConfigurationProperties({
        HigestAuthorityProperties.class,
})
public class PropertiesConfig {
}

1.5、重写loadUserByUsername方法

/**
 * @author 木字楠
 * @version 1.0
 * @description 自定义服务实现
 * @date 2022/8/12
 */
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    @Resource
    private HttpServletRequest request;
    private final UserAuthService userAuthService;
    private final UserInfoService userInfoService;
    private final RoleService roleService;
    private final MenuService menuService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserAuth userAuth = usernamePasswordCheck(username);
        return convertToUser(request, userAuth);
    }

    /**
     * 用户数据转护
     *
     * @param request  request
     * @param userAuth 用户权限信息
     * @return {@link User} 用户信息
     */
    private User convertToUser(HttpServletRequest request, UserAuth userAuth) {
        User user = BeanCopyUtil.copyObject(userAuth, User.class);
        // 查询用户基础信息
        UserInfo userInfo = userInfoService.getOne(new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getId, userAuth.getUserInfoId()));

        //region 获取用户Ip相关信息
        String ipAddress = IpUtil.getIpAddress(request);
        String ipSource = IpUtil.getIpSource(ipAddress);
        UserAgent userAgent = IpUtil.getUserAgent(request);
        //endregion

        //region 获取用户权限信息
        Set<String> permisstionSet = new HashSet<>(99);
        Role role = roleService.getOne(new LambdaQueryWrapper<Role>().eq(Role::getId, userAuth.getUserRoleId()));
        if (null != role) {
            permisstionSet = menuService.getUserPermissionList(role);
        }
        //endregion

        //数据封装
        String visitor = "游客";
        user.setNickname(userInfo.getNickname());
        user.setAvatar(userInfo.getAvatar());
        user.setGender(GenderEnum.getGenderValueByCode(userInfo.getGender()));
        user.setEmail(userInfo.getEmail());
        user.setPersonIntro(user.getPersonIntro());
        user.setIpAddress(ipAddress);
        user.setIpSource(ipSource);
        user.setGmtCreate(userInfo.getGmtCreate());
        user.setGmtUpdate(userInfo.getGmtUpdate());
        user.setLastLoginTime(userInfo.getLastLoginTime());
        user.setRole(null == role ? visitor : role.getRoleName());
        user.setPermissionList(permisstionSet);
        user.setOs(userAgent.getOperatingSystem().getName());
        user.setBrower(userAgent.getBrowser().getName());

        return user;
    }

    /**
     * 用户名密码校验
     */
    private UserAuth usernamePasswordCheck(String username) {
        // 用户名非空校验
        if (StringUtils.isEmpty(username)) {
            throw new BaseException(HttpCodeEnum.USERNAME_CAN_NOT_BE_EMPTY.getMessage());
        }

        // 查询用户信息
        UserAuth userAuth = userAuthService.getOne(new LambdaQueryWrapper<UserAuth>().eq(UserAuth::getUsername, username)
                .eq(UserAuth::getIsDisabled, SystemConstant.NOT_DISABLED)
                .eq(UserAuth::getIsDeleted, SystemConstant.NOT_DELETED));

        if (null == userAuth) {
            throw new BaseException(HttpCodeEnum.USERNAME_OR_PASSWORD_ERROR.getMessage());
        }

        return userAuth;
    }

}

1.6、自定义匿名访问注解

自定义匿名访问注解,届时我们会使用SpringSecurity拦截所有请求,未登录用户访问接口时将会统一返回 请登陆后再进行访问!,而带有匿名注解的接口将会被放行,无论用户是否登录,都可以正常访问。
/**
 * @author 木字楠
 * @version 1.0
 * @description 匿名访问注解
 * @date 2022/8/12
 */
public @interface Anonymous {
}
    private final ApplicationContext applicationContext;
    /**
     * 查找可以匿名访问的接口
     *
     * @return 匿名访问接口集合
     */
    private Set<String> listAnonymous() {
        Map<RequestMappingInfo, HandlerMethod> handlerMethods =
                applicationContext.getBean(RequestMappingHandlerMapping.class).getHandlerMethods();
        Set<String> anonymousUrls = new HashSet<>();
        for (Map.Entry<RequestMappingInfo, HandlerMethod> infoEntry : handlerMethods.entrySet()) {
            HandlerMethod handlerMethod = infoEntry.getValue();
            AnonymousAccess anonymousAccess = handlerMethod.getMethodAnnotation(AnonymousAccess.class);
            if (anonymousAccess != null) {
                assert infoEntry.getKey().getPatternsCondition() != null;
                anonymousUrls.addAll(infoEntry.getKey().getPatternsCondition().getPatterns());
            }
        }
       System.out.println("可以匿名访问的url:{}", anonymousUrls);
        return anonymousUrls;
    }

1.8、编写SpringSecurity配制类

自定义配制类继承 WebSecurityConfigurerAdapter,并且重写其中的三个 config方法

在这里插入图片描述
在这里插入图片描述

public void configure(WebSecurity web) 内部重新实现,主要放行静态资源
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().mvcMatchers(
                "/js/**", "/css/**", "/img/**", "/fonts/**",
                "/", "/index.html", "/favicon.ico", "/doc.html",
                "/swagger-ui.html", "/webjars/**", "/swagger-resources/**", "/v3/**");
    }
protected void configure(AuthenticationManagerBuilder auth) 内部重新实现,指定UserDetailsService执行方式以及密码加密方式
    private final UserDetailsServiceImpl userDetailsService;
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
相关实践学习
借助OSS搭建在线教育视频课程分享网站
本教程介绍如何基于云服务器ECS和对象存储OSS,搭建一个在线教育视频课程分享网站。
目录
相关文章
|
6月前
|
SQL XML Java
若依框架 --- 使用数据权限功能
若依框架 --- 使用数据权限功能
841 0
|
6月前
|
前端开发 数据安全/隐私保护
若依框架---权限控制角色设计
若依框架---权限控制角色设计
1130 0
|
前端开发
jeecgboot数据权限用法
jeecgboot数据权限用法
1116 0
jeecgboot数据权限用法
|
6月前
|
存储 缓存 数据库
Shiro【核心功能、核心组件、项目搭建 、配置文件认证、数据库认证 】(一)-全面详解(学习总结---从入门到深化)
Shiro【核心功能、核心组件、项目搭建 、配置文件认证、数据库认证 】(一)-全面详解(学习总结---从入门到深化)
286 1
|
6月前
|
SQL 前端开发 数据安全/隐私保护
若依框架---权限管理设计
若依框架---权限管理设计
534 0
|
存储 运维 监控
三大架构图—结合若依权限管理系统
最近在进行架构师的培训,结合具体的项目也许能够帮助我们更好的理解架构对我们而言的意义,今天我着重从技术架构图来讨论架构图的具体应用
|
JavaScript 数据安全/隐私保护
【Vue 开发实战】实战篇 # 33:更加精细化的权限设计(权限组件、权限指令)
【Vue 开发实战】实战篇 # 33:更加精细化的权限设计(权限组件、权限指令)
141 0
【Vue 开发实战】实战篇 # 33:更加精细化的权限设计(权限组件、权限指令)
|
数据库 数据安全/隐私保护
后台基础权限框架搭建实现
后台基础权限框架搭建实现
125 0
后台基础权限框架搭建实现
|
数据库 数据安全/隐私保护
5、后台基础权限框架搭建实现[木字楠博客]
5、后台基础权限框架搭建实现[木字楠博客]
89 0
5、后台基础权限框架搭建实现[木字楠博客]
下一篇
无影云桌面