spring boot整合spring security(二)--手机号验证码登陆

概述

spring security默认使用的是用户名密码的登陆,如果想要增加一种登陆方式,

就要仿照源码中用户名密码登陆的方式, 手写一套手机号验证码登陆校验, 话不多说, 开干!

搭建基础架构

搭建项目基础架构工作, 在(一)中已经完成, 这里直接在其基础上继续开发

认证流程

  • 1.进入 UsernamePasswordAuthenticationFilter 然后构建一个没有认证的 UsernamePasswordAuthenticationToken
  • 2.随后交给 AuthenticationManager 进行验证,
  • 3.AuthenticationManager 找到对应的 AuthenticationProvider进行认证
  • 4.AuthenticationProvider找到上下文中的UserDetailsService 中寻找用户然后对比
  • 5.验证成功返回 Authentication 放入 SecurityContextHolder中

这里需要自定义一个token,filter和provider

定义token

参考UsernamePasswordAuthenticationToken, 其中principal是用户名, credentials是密码,
由于这里只有手机号, 不需要密码, 所以, 将其中有关credentials的去除, 复制下来即可
在这里插入图片描述

自定义SmsCodeAuthenticationToken如下

package com.etouch.security.security.smslogin;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.SpringSecurityCoreVersion;

import java.util.Collection;

/*
 *这一步的作用是为了替换原有系统的 UsernamePasswordAuthenticationToken 用来做验证
 *
 * 代码都是从UsernamePasswordAuthenticationToken 里粘贴出来的
 *
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;

    /**
     * 在 UsernamePasswordAuthenticationToken 中该字段代表登录的用户名,
     * 在这里就代表登录的手机号码
     */
    private final Object principal;

    /**
     * 构建一个没有鉴权的 SmsCodeAuthenticationToken
     */
    public SmsCodeAuthenticationToken(Object principal) {
        super(null);
        this.principal = principal;
        setAuthenticated(false);
    }

    /**
     * 构建拥有鉴权的 SmsCodeAuthenticationToken
     */
    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true); // must use super, as we override
    }



    // ~ Methods
    // 剩下的方法不用动就行了 就是从 UsernamePasswordAuthenticationToken 里粘贴出来的
    // ========================================================================================================

    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException(
                    "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        }

        super.setAuthenticated(false);
    }

    @Override
    public void eraseCredentials() {
        super.eraseCredentials();
    }
}

定义过滤器filter

同样参照UsernamePasswordAuthenticationFilter,将和password有关的去除, 略微修改以下, 即可完成

同样参照UsernamePasswordAuthenticationFilter图片位

自定义的SmsCodeAuthenticationFilter代码如下

package com.etouch.security.security.smslogin;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 短信登录的鉴权过滤器,模仿 UsernamePasswordAuthenticationFilter 实现
*/
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    /**
     * form表单中手机号码的字段name
     */
    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "phone";

    private String mobileParameter = SPRING_SECURITY_FORM_MOBILE_KEY;
    /**
     * 是否仅 POST 方式
     */
    private boolean postOnly = true;

    public SmsCodeAuthenticationFilter() {
        // 短信登录的请求 post 方式的 /sms/login
        super(new AntPathRequestMatcher("/sys/login/phone", "POST"));
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        if (postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException(
                    "Authentication method not supported: " + request.getMethod());
        }

        // 电话号码
        String mobile = obtainUsername(request);
        if (StringUtils.isEmpty(mobile)) {
            throw new AuthenticationServiceException("电话号码不能为空");
        }
        return this.getAuthenticationManager().authenticate(new SmsCodeAuthenticationToken(mobile));
    }


    protected String obtainUsername(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }

    protected String obtainPassword(HttpServletRequest request) {
        return request.getParameter(mobileParameter);
    }
}

接下来是自定义手机号验证码登陆鉴证器provider

代码如下

package com.etouch.security.security.smslogin;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;
import java.util.Objects;

/**
 * 短信登陆鉴权 Provider,要求实现 AuthenticationProvider 接口
 */
@Configuration
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken authenticationToken = (SmsCodeAuthenticationToken) authentication;

        String mobile = (String) authenticationToken.getPrincipal();

        checkSmsCode(mobile);

        UserDetails userDetails = userDetailsService.loadUserByUsername(mobile);
        if (userDetails == null){
            throw new InternalAuthenticationServiceException("无法获取用户信息");
        }
        // 此时鉴权成功后,应当重新 new 一个拥有鉴权的 authenticationResult 返回
        SmsCodeAuthenticationToken authenticationResult = new SmsCodeAuthenticationToken(userDetails, userDetails.getAuthorities());

        authenticationResult.setDetails(authenticationToken.getDetails());

        return authenticationResult;
    }

    private void checkSmsCode(String mobile) {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
      
        String inputCode = request.getParameter("smsCode");
        
        //这里的验证码我们放session里,这里拿出来跟用户输入的做对比
        Map<String, Object> smsLogin = (Map<String, Object>) request.getSession().getAttribute("smsLogin");
        if (smsLogin == null) {
            throw new BadCredentialsException("未检测到申请验证码");
        }

        String applyMobile = (String) smsLogin.get("phone");

        int code = (int) smsLogin.get("smsCode");

        if (!applyMobile.equals(mobile)) {
            throw new BadCredentialsException("申请的手机号码与登录手机号码不一致");
        }
        if (code != Integer.parseInt(inputCode)) {
            throw new BadCredentialsException("验证码错误");
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // 判断 authentication 是不是 SmsCodeAuthenticationToken 的子类或子接口
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }
}


接下来是配置securityConfig

package com.etouch.security.security;

import com.etouch.security.security.handler.*;
import com.etouch.security.pojo.entity.SysPermission;
import com.etouch.security.security.smslogin.SmsCodeAuthenticationFilter;
import com.etouch.security.security.smslogin.SmsCodeAuthenticationProvider;
import com.etouch.security.service.SysPermissionService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.builders.WebSecurity;
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.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler;

import java.util.List;

/**
 * UsernamePasswordAuthenticationFilter拦截登录请求
 * UsernamePasswordAuthenticationFilter获取到用户名和密码构造一个UsernamePasswordAuthenticationToken传入AuthenticationManager
 * AuthenticationManager找到对应的Provider进行具体校验逻辑处理
 * 最后登录信息保存进SecurityContext
 *
 * @author chenyunchang
 */
@Configuration
@EnableWebSecurity
//开启Security注解(使用接口上的注解来控制访问权限)
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
@Slf4j
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //登录成功处理
    @Autowired
    private SuccessAuthenticationHandler successAuthenticationHandler;

    //登录失败处理
    @Autowired
    private FailureAuthenticationHandler failureAuthenticationHandler;

    //未登录处理
    @Autowired
    private CustomAuthenticationEntryPoint customAuthenticationEntryPoint;

    //没有权限处理
    @Autowired
    private AuthAccessDeniedHandler authAccessDeniedHandler;

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    @Autowired
    private SysPermissionService sysPermissionService;

    @Autowired
    private SmsCodeAuthenticationProvider smsCodeAuthenticationProvider;

    @Autowired
    private MyLogoutSuccessHandler myLogoutSuccessHandler;


    /**
     * 注入身份管理器bean
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 密码加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        /**
         * BCryptPasswordEncoder:相同的密码明文每次生成的密文都不同,安全性更高
         */
        return new BCryptPasswordEncoder();
    }

    /**
     * 表达式	说明
     * hasRole([role])	用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀)
     * hasAnyRole([role1,role2])	用户拥有任意一个制定的角色时返回true
     * hasAuthority([authority])	等同于hasRole,但不会带有ROLE_前缀
     * asAnyAuthority([auth1,auth2])	等同于hasAnyRole
     * permitAll	永远返回true
     * denyAll	永远返回false
     * authentication	当前登录用户的authentication对象
     * fullAuthenticated	当前用户既不是anonymous也不是rememberMe用户时返回true
     * hasIpAddress('192.168.1.0/24'))	请求发送的IP匹配时返回true
     */
    /**
     * Http安全配置
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable();
        //配置自定义登陆路径
        http.formLogin()
                //登陆接口
                .loginProcessingUrl("/sys/login")
                //自定义登陆失败处理
                .failureHandler(failureAuthenticationHandler)
                //自定义登陆成功处理
                .successHandler(successAuthenticationHandler)
                //以下是异常处理器
                .and().exceptionHandling()
                //未登录自定义返回
                .authenticationEntryPoint(customAuthenticationEntryPoint)
                //没有权限访问处理
                .accessDeniedHandler(authAccessDeniedHandler)
                .and().logout().logoutUrl("/sys/logout")
        ;
        //短信验证码登陆
//        http.apply(smsCodeAuthenticationSecurityConfig)
//                .and().formLogin().loginProcessingUrl("/sys/login/phone")
//        ;
        //短信验证码登陆验证
        //添加手机号登陆过滤器
        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserDetailsService(userDetailsService);
        http.authenticationProvider(smsCodeAuthenticationProvider)
                .addFilterAfter(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        //查询所有权限,动态权限认证
        List<SysPermission> permissions = sysPermissionService.list();
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry authorizeRequests = http
                .authorizeRequests();
        permissions.forEach(permission ->
        {
            log.info("获取权限为" + permission.getPermCode());
            //将连接地址对应的权限存入
            authorizeRequests.antMatchers(permission.getUrl()).hasAnyAuthority(permission.getPermCode());
        });
        //配置无需认证的访问路径
        http.authorizeRequests()
                // 跨域预检请求
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                // 登录URL
                .antMatchers("/login/**").permitAll()
                .antMatchers("/sms/**").permitAll()
                // swagger
                .antMatchers("/swagger**/**").permitAll()
                .antMatchers("/webjars/**").permitAll()
                .antMatchers("/v2/**").permitAll()
                // 其他所有请求需要身份认证
                .anyRequest().authenticated();

        // 退出登录处理器
        http.logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler(myLogoutSuccessHandler);
    }

    /**
     * 配置无需登陆就可以访问的路径
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //allow Swagger URL to be accessed without authentication
        web.ignoring().antMatchers(
                //swagger api json
                "/v2/api-docs",
                //用来获取支持的动作
                "/swagger-resources/configuration/ui",
                //用来获取api-docs的URI
                "/swagger-resources",
                //安全选项
                "/swagger-resources/configuration/security",
                "/swagger-ui.html",
                "/doc.html",
                "/css/**", "/js/**"
        );
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 这里要设置自定义认证
        //手机号验证
        auth.authenticationProvider(smsCodeAuthenticationProvider);
    }
}


修改UserDetailService的实现类为

package com.etouch.security.security;

import com.etouch.security.pojo.dto.SysRoleDTO;
import com.etouch.security.pojo.dto.SysUserDTO;
import com.etouch.security.service.SysUserService;
import com.etouch.security.util.MobileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 自定义的认证用户获取服务类
 */
@Service
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 根据用户名获取认证用户信息
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        if (StringUtils.isEmpty(username)) {
            log.info("UserDetailsService没有接收到用户账号");
            throw new UsernameNotFoundException("UserDetailsService没有接收到用户账号");
        } else {
            //根据用户名查找用户信息
            SysUserDTO sysUserDTO = null;
            if (MobileUtil.isMobileNO(username)) {
                //手机号验证码登陆
                sysUserDTO = sysUserService.getUserByPhone(username);
            } else {
                //用户名, 密码登陆
                sysUserDTO = sysUserService.getUserByUserName(username);
            }
            if (sysUserDTO == null) {
                throw new UsernameNotFoundException(String.format("用户不存在", username));
            }
            //新建权限集合
            List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
            //模拟从数据库获取角色权限
            List<SysRoleDTO> sysRoleDTOList = sysUserDTO.getSysRoleDTOList();
            for (SysRoleDTO sysRoleDTO : sysRoleDTOList) {
                //封装用户信息和角色信息到SecurityContextHolder全局缓存中
                grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_" + sysRoleDTO.getRoleName()));
            }

            //创建一个用于认证的用户对象并返回,包括:用户名,密码,角色
            return new User(sysUserDTO.getUsername(), sysUserDTO.getPassword(), grantedAuthorities);
        }
    }
}

生成验证码接口

package com.etouch.security.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/sms")
@Slf4j
@Api(tags = "短信接口")
public class SmsController {

    @ApiOperation("发送短信验证码")
    @RequestMapping(value = "/code",method = RequestMethod.POST)
    public String sms(String phone, HttpServletRequest request) {
        HttpSession session = request.getSession();
        int smsCode = (int) Math.ceil(Math.random() * 9000 + 1000);
        Map<String, Object> map = new HashMap<>(16);
        map.put("phone", phone);
        map.put("smsCode", smsCode);
        session.setAttribute("smsLogin", map);
        log.info("{}:为 {} 设置短信验证码:{}", session.getId(), phone, smsCode);
        return "你的手机号"+phone+"验证码是"+smsCode;
    }


}

登陆接口

package com.etouch.security.controller;

import com.etouch.security.exception.ExceptionEnum;
import com.etouch.security.exception.ProjectException;
import com.etouch.security.pojo.dto.SysUserDTO;
import com.etouch.security.security.smslogin.SmsCodeAuthenticationToken;
import com.etouch.security.service.SysUserService;
import com.etouch.security.util.ResultUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.catalina.security.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.security.Security;

import static org.springframework.security.web.context.HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;

/**
 * @author chenyunchang
 * @title
 * @date 2020/10/28 11:10
 * @Description:
 */
@RestController
@RequestMapping
@Api(tags = "登陆相关接口")
public class LoginController {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;


    @ApiOperation("用户名,密码登陆")
    @PostMapping("/login")
    public ResultUtils<SysUserDTO> login(@RequestParam String username, @RequestParam String password, HttpServletRequest request) {
        SysUserDTO sysUserDTO = sysUserService.getUserByUserName(username);
        if (sysUserDTO == null) {
            throw new ProjectException(ExceptionEnum.USER_NOT_FOUND);
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(password, sysUserDTO.getPassword());
        if (!matches) {
            throw new ProjectException(ExceptionEnum.USERNAME_OR_PASSWORD_ERRO);
        }
        sysUserDTO.setPassword(null);
        // 系统登录认证
        return ResultUtils.success("登陆成功", sysUserDTO);
    }

    @ApiOperation("手机号, 验证码登陆")
    @PostMapping("/login/phone")
    public ResultUtils<SysUserDTO> loginByPhoneAndCode(String phone, String smsCode, HttpServletRequest request) {
        //security已经错过校验, 这里不再校验验证码
        SysUserDTO sysUserDTO = sysUserService.getUserByPhone(phone);
        if (sysUserDTO == null) {
            throw new ProjectException(ExceptionEnum.USER_NOT_FOUND);
        }
        sysUserDTO.setPassword(null);
        //进行手动security登陆
        UserDetails userDetails = userDetailsService.loadUserByUsername(sysUserDTO.getUsername());
        SmsCodeAuthenticationToken smsCodeAuthenticationToken = new SmsCodeAuthenticationToken(phone, userDetails.getAuthorities());
        Authentication authenticate = authenticationManager.authenticate(smsCodeAuthenticationToken);
        SecurityContext context = SecurityContextHolder.getContext();
        context.setAuthentication(authenticate);
        HttpSession session = request.getSession(true);
        //在session中存放security context,方便同一个session中控制用户的其他操作
        session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, context);
        return ResultUtils.success("登陆成功", sysUserDTO);
    }
}


测试

未登录状态

在这里插入图片描述

用户名 密码登陆

在这里插入图片描述

手机号 验证码登陆

测试前, 先退出
在这里插入图片描述
再次访问任意接口

在这里插入图片描述
提示, 未登录, 证明退出登陆成功

接下来, 进行手机号, 验证码登陆
在这里插入图片描述
未完待续…

  • 2
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
entfrm-boot是一个以模块化为核心的无代码开发平台,是一个集PC和APP快速开发、系统管理、运维监控、开发工具、OAuth2授权、可视化数据源管理与数据构建、API动态生成与统计、工作流、智能表单设计、微服务骨架等全方位功能于一体的高效、稳定的快速开发平台。 后端采用Spring Boot 2.X 、Spring Security、Oauth2、Mybatis Plus、Activiti、 uni-app等核心技术,前端基于vue-element-admin框架。 entfrm-boot可视化开发平台功能: 1、系统管理 机构管理:配置系统组织机构,无限级树结构展现支持数据权限。 角色管理:角色菜单权限分配、设置角色按机构进行数据范围权限划分。 用户管理:用户是系统操作者,该功能主要完成系统用户配置。 菜单管理:配置系统菜单,操作权限,按钮权限标识等。 字典管理:对系统中经常使用的一些较为固定的数据进行维护。 参数管理:对系统动态配置常用参数。 快捷方式:添加顶部、首页快捷方式。 文件管理:文件统一管理,集成阿里云、腾讯云、七牛等。 终端管理:OAuth2 Password、授权码模式,灵活管理。 数据维护:mysql数据库备份与还原。 2、运维监控 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。 令牌管理:用户登录token管理。 数据监控: 监视当前系统数据库连接池状态,可进行分析SQL找出系统性能瓶颈。 缓存监控:redis缓存监控。 服务监控: 监视当前系统CPU、内存、磁盘、堆栈等相关信息。 API监控:restful api 接口调用统计、可视化。 日志监控 登录日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。 操作日志:系统登录日志记录查询包含登录异常。 3、消息系统 消息模板:消息模板管理与审核。 系统消息:包括系统通知、告警等。 短信:短信平台集成。 邮件:邮件集成。 4、开发工具 数据管理:Mysql、SQL Server、PostgreSQL等主流数据库在线管理、数据表管理(新建、修改、删除、查询)、数据源管理与数据转换。 代码生成:支持单表、树表、左树右表代码生成。 5、API引擎 应用管理:应用新增、修改、删除、查看。 API设计:API在线设计、无代码开发,支持自定义。 API文档与测试:API文档生成与自动化测试。 API资源权限:API资源管理、权限控制。 API统计报表:API使用统计、图表展示。 6、流程引擎(Activiti) 模型管理:功能包括模型新增、查看、删除、模型设计、模型部署。 流程定义:功能包括流程图查看、流程挂起与激活、流程删除。 请假管理:功能包括请假新增、编辑、查看、删除、导出、提交。 流程任务:功能包括流程审批、审批意见列表及流程追踪。 7、表单引擎 表单设计:表单快速设计。 多终端支持:PC、Pad、手机多端适配。 表单分享:表单一键分享。 表单数据统计:手机填写的表单,支持列表和图表统计。 uni-app示例与基础:提供uni-app实例,并集成表单功能。 8、数据引擎 可视化:快速配置,图表生成。 大屏:图表编排,大屏显示。 报表:报表设计。 9、拓展插件 10、分布式与微服务 开发环境: 开发工具 IntelliJ IDEA、Navicat Premium 后端技术 Springboot2.x、SpringSecurity、Oauth2、JWT、mybatis-plus、activiti、Flutter、Mysql等 前端技术 Node.js 10+、Vue.js 2.6+、Vue CLI、Vuex、VueRouter、Element-UI等 entfrm-boot可视化开发平台使用说明: 1、配置环境(jdk1.8、maven3.x、mysql5.6及以上、redis3.2及以上) 2、创建数据库 3、初始化sql脚本:entfrm.sql 4、导入项目到IntelliJ IDE中 5、修改配置文件entfrm-boot/entfrm-web/src/main/resources/application.yml redis 配置 datasource 数据源配置 6、启动WebApplication 7、启动entfrm-ui 在线演示 账号:entfrm 密码:123456
mallcloud商城基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba并采用前后端分离vue的企业级微服务敏捷开发系统架构。并引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手容易,适合学习和企业中使用。真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案,面向互联网设计同时适合B端和C端用户,支持CI/CD多环境部署,并提供应用管理方便第三方系统接入;同时还集合各种微服务治理功能和监控功能。模块包括:企业级的认证系统、开发平台、应用监控、慢sql监控、统一日志、单点登录、Redis分布式高速缓存、配置中心、分布式任务调度、接口文档、代码生成等等。 mallcloud商城特点: 1、前后端分离的企业级微服务架构 2、基于Spring Boot 2.0.X、Spring Cloud Finchley和Spring Cloud Alibaba 3、深度定制Spring Security真正实现了基于RBAC、jwt和oauth2的无状态统一权限认证的解决方案 4、提供应用管理,方便第三方系统接入 5、引入组件化的思想实现高内聚低耦合,项目代码简洁注释丰富上手容易 6、注重代码规范,严格控制包依赖,每个工程基本都是最小依赖 7、非常适合学习和企业中使用 mallcloud商城功能: 1、统一认证功能 支持oauth2的四种模式登录 支持用户名、密码加图形验证码登录 支持手机号加密码登录 支持openId登录 支持第三方系统单点登录 2、分布式系统基础支撑 服务注册发现、路由与负载均衡 服务降级与熔断 服务限流(url/方法级别) 统一配置中心 统一日志中心 统一分布式缓存操作类、cacheManager配置扩展 分布式锁 分布式任务调度器 支持CI/CD持续集成(包括前端和后端) 分布式高性能Id生成器 分布式事务 3、系统监控功能 服务调用链监控 应用拓扑图 慢服务检测 服务Metric监控 应用监控(应用健康、JVM、内存、线程) 错误日志查询 慢查询SQL监控 应用吞吐量监控(qps、rt) 服务降级、熔断监控 服务限流监控 分库分表、读写分离 4、业务基础功能支撑 高性能方法级幂等性支持 RBAC权限管理,实现细粒度控制(方法、url级别) 快速实现导入、导出功能 数据库访问层自动实现crud操作 代码生成器 基于Hutool的各种便利开发工具 网关聚合所有服务的Swagger接口文档 统一跨域处理 统一异常处理 mallcloud商城演示地址 账号密码:admin/admin 应用监控账号密码:admin/admin 配置中心账号密码:nacos/nacos APM监控账号密码:admin/admin Grafana账号:mall/mall txlcn事务管理器密码:admin 任务管理账号密码:admin/123456

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

意田天

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

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

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

打赏作者

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

抵扣说明:

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

余额充值