返回顶部
首页 > 资讯 > 精选 >Sping Security前后端分离怎么实现
  • 632
分享到

Sping Security前后端分离怎么实现

2023-07-05 16:07:57 632人浏览 安东尼
摘要

本篇内容主要讲解“Sping Security前后端分离怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Sping Security前后端分离怎么实现”吧!spring

本篇内容主要讲解“Sping Security前后端分离怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Sping Security前后端分离怎么实现”吧!

spring Seciruty简单介绍

Spring Security是基于Spring框架,提供了一套WEB应用安全性的完整解决方案。关于安全方面的两个核心功能是认证和授权,Spring Security重要核心功能就是实现用户认证(Authentication)和用户授权(Authorization)。

认证(Authentication)

认证是用来验证某个用户能否访问该系统。用户认证一般要求用户提供用户名和密码,系统通过校验用户名和密码来完成认证过程。

授权(Authorization)

授权发生在认证之后,用来验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。

实现简单介绍

Spring Security进行认证和鉴权的时候,采用的一系列的Filter来进行拦截的。 下图是Spring Security基于表单认证授权的流程,

Sping Security前后端分离怎么实现

在Spring Security一个请求想要访问到api就会从左到右经过蓝线框里的过滤器,其中绿色部分是负责认证的过滤器,蓝色部分是负责异常处理,橙色部分则是负责授权。进过一系列拦截最终访问到我们的API。

准备阶段

整个项目结构如下,demo1部分是基于表单的认证,demo2部分是基于Token的认证,数据库采用是Mysql,访问数据库使用的JPA,Spring Boot版本是2.7.8,Spring Security版本是比较新的5.7.6,这里需要注意的是Spring Security5.7以后版本和前面的版本有一些差异,未来该Demo的版本的问题一直会持续保持升级。 后续也会引用前端项目,前端后台管理部分我个人感觉后端程序员也要进行简单的掌握一些,便于工作中遇到形形色色问题更好的去处理。

Sping Security前后端分离怎么实现

Maven

关于Maven部分细节这里不进行展示了,采用的父子工程,主要简单看下依赖的版本,

    <properties>        <maven.compiler.source>8</maven.compiler.source>        <maven.compiler.target>8</maven.compiler.target>        <SpringBoot.vetsion>2.7.8</springboot.vetsion>        <Mysql-connector-java.version>5.1.46</mysql-connector-java.version>        <org.projectlombok.version>1.16.14</org.projectlombok.version>        <jJwt.version>0.11.1</jjwt.version>        <fastJSON.version>1.2.75</fastjson.version>    </properties>

统一错误码

public enum ResultCode {        SUCCESS(200, "成功"),        COMMON_FaiL(999, "失败"),        PARAM_NOT_VALID(1001, "参数无效"),    PARAM_IS_BLANK(1002, "参数为空"),    PARAM_TYPE_ERROR(1003, "参数类型错误"),    PARAM_NOT_COMPLETE(1004, "参数缺失"),        USER_NOT_LOGIN(2001, "用户未登录"),    USER_ACCOUNT_EXPIRED(2002, "账号已过期"),    USER_CREDENTIALS_ERROR(2003, "密码错误"),    USER_CREDENTIALS_EXPIRED(2004, "密码过期"),    USER_ACCOUNT_DISABLE(2005, "账号不可用"),    USER_ACCOUNT_LOCKED(2006, "账号被定"),    USER_ACCOUNT_NOT_EXIST(2007, "账号不存在"),    USER_ACCOUNT_ALREADY_EXIST(2008, "账号已存在"),    USER_ACCOUNT_USE_BY_OTHERS(2009, "账号下线"),        NO_PERMISSION(3001, "没有权限");    private Integer code;    private String message;    ResultCode(Integer code, String message) {        this.code = code;        this.message = message;    }    public Integer getCode() {        return code;    }    public void setCode(Integer code) {        this.code = code;    }    public String getMessage() {        return message;    }    public void setMessage(String message) {        this.message = message;    }    private static Map<Integer, ResultCode> map = new HashMap<>();    private static Map<String, ResultCode> descMap = new HashMap<>();    static {        for (ResultCode value : ResultCode.values()) {            map.put(value.getCode(), value);            descMap.put(value.getMessage(), value);        }    }    public static ResultCode getByCode(Integer code) {        return map.get(code);    }    public static ResultCode getByMessage(String desc) {        return descMap.get(desc);    }}

统一返回定义

public class CommonResponse<T> implements Serializable {        private final static String SUCCESS_CODE = "SUCCESS";        private String message;        private T data;        private Integer code;        private Boolean state;        private String detailMessage;        public static <T> CommonResponse<T> ok() {        return ok(null);    }        public static <T> CommonResponse<T> ok(T data) {        CommonResponse<T> response = new CommonResponse<T>();        response.code = ResultCode.SUCCESS.getCode();        response.data = data;        response.message = "返回成功";        response.state = true;        return response;    }        public static <T> CommonResponse<T> error(Integer code, String message) {        return error(code, message, null);    }        public static <T> CommonResponse<T> error(Integer code, String message,                                              String detailMessage) {        CommonResponse<T> response = new CommonResponse<T>();        response.code = code;        response.data = null;        response.message = message;        response.state = false;        response.detailMessage = detailMessage;        return response;    }    public Boolean getState() {        return state;    }    public CommonResponse<T> setState(Boolean state) {        this.state = state;        return this;    }    public String getMessage() {        return message;    }    public CommonResponse<T> setMessage(String message) {        this.message = message;        return this;    }    public T getData() {        return data;    }    public CommonResponse<T> setData(T data) {        this.data = data;        return this;    }    public Integer getCode() {        return code;    }    public CommonResponse<T> setCode(Integer code) {        this.code = code;        return this;    }    public String getDetailMessage() {        return detailMessage;    }    public CommonResponse<T> setDetailMessage(String detailMessage) {        this.detailMessage = detailMessage;        return this;    }}

数据库设计

基于RBAC模型最简单奔版本的数据库设计,用户、角色、权限表;

Sping Security前后端分离怎么实现

基于表单认证

对于表单认证整体过程可以参考下图,

Sping Security前后端分离怎么实现

核心配置

核心配置包含了框架开启以及权限配置,这部分内容是重点要关注的,这里可以看到所有重写的内容,主要包含以下七方面内容:

  • 定义哪些资源不需要认证,哪些需要认证,这里我采用注解形式;

  • 实现自定义认证以及授权异常的接口;

  • 实现自定义登录成功以及失败的接口;

  • 实现自定义登出以后的接口;

  • 实现自定义重数据查询对应的账号权限的接口;

  • 自定义加密的Bean;

  • 自定义授权认证Bean;

当然Spring Security还支持更多内容,比如限制用户登录个数等等,这里部分内容使用不是太多,后续大家如果有需要我也可以进行补充。

//Spring Security框架开启@EnableWebSecurity//授权全局配置@EnableGlobalMethodSecurity(prePostEnabled = true)@Configurationpublic class SecurityConfig {    @Autowired    private SysUserService sysUserService;    @Autowired    private NotAuthenticationConfig notAuthenticationConfig;    @Bean    SecurityFilterChain filterChain(httpsecurity Http) throws Exception {        //支持跨域        http.cors().and()                //csrf关闭                .csrf().disable()                //配置哪些需要认证 哪些不需要认证                .authorizeRequests(rep -> rep.antMatchers(notAuthenticationConfig.getPermitAllUrls().toArray(new String[0]))                        .permitAll().anyRequest().authenticated())                .exceptionHandling()                //认证异常处理                .authenticationEntryPoint(new ResourceAuthExceptionEntryPoint())                //授权异常处理                .accessDeniedHandler(new CustomizeAccessDeniedHandler())                //登录认证处理                .and().fORMLogin()                .successhandler(new CustomizeAuthenticationSuccessHandler())                .failureHandler(new CustomizeAuthenticationFailureHandler())                //登出                .and().loGout().permitAll().addLogoutHandler(new CustomizeLogoutHandler())                .logoutSuccessHandler(new CustomizeLogoutSuccessHandler())                .deleteCookies("JSESSIONID")                //自定义认证                .and().userDetailsService(sysUserService);        return http.build();    }    @Bean    public PassWordEncoder passwordEncoder() {        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();        return bCryptPasswordEncoder;    }    @Bean("ssc")    public SecuritySecurityCheckService permissionService() {        return new SecuritySecurityCheckService();    }    }

通过注解形式实现哪些需要资源不需要认证

通过自定义注解@NotAuthentication,然后通过实现InitializingBean接口,实现加载不需要认证的资源,支持类和方法,使用就是通过在方法或者类打上对应的注解。

@Documented@Retention(RetentionPolicy.RUNTIME)@Target({ ElementType.METHOD, ElementType.TYPE })public @interface NotAuthentication {}@Servicepublic class NotAuthenticationConfig implements InitializingBean, ApplicationContextAware {    private static final String PATTERN = "\\{(.*?)}";    public static final String ASTERISK = "*";    private ApplicationContext applicationContext;    @Getter    @Setter    private List<String> permitAllUrls = new ArrayList<>();    @Override    public void afterPropertiesSet() throws Exception {        RequestMappingHandlerMapping mapping = applicationContext.getBean(RequestMappingHandlerMapping.class);        Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();        map.keySet().forEach(x -> {            HandlerMethod handlerMethod = map.get(x);            // 获取方法上边的注解 替代path variable 为 *            NotAuthentication method = AnnotationUtils.findAnnotation(handlerMethod.getMethod(), NotAuthentication.class);            Optional.ofNullable(method).ifPresent(inner -> Objects.requireNonNull(x.getPathPatternsCondition())                    .getPatternValues().forEach(url -> permitAllUrls.add(url.replaceAll(PATTERN, ASTERISK))));            // 获取类上边的注解, 替代path variable 为 *            NotAuthentication controller = AnnotationUtils.findAnnotation(handlerMethod.getBeanType(), NotAuthentication.class);            Optional.ofNullable(controller).ifPresent(inner -> Objects.requireNonNull(x.getPathPatternsCondition())                    .getPatternValues().forEach(url -> permitAllUrls.add(url.replaceAll(PATTERN, ASTERISK))));        });    }    @Override    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {        this.applicationContext = applicationContext;    }}

自定义认证异常实现

AuthenticationEntryPoint�用来解决匿名用户访问无权限资源时的异常。

public class ResourceAuthExceptionEntryPoint implements AuthenticationEntryPoint {    @Override    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {        CommonResponse result= CommonResponse.error(ResultCode.USER_NOT_LOGIN.getCode(),                ResultCode.USER_NOT_LOGIN.getMessage());        response.setCharacterEncoding("UTF-8");        response.setContentType("application/json; charset=utf-8");        response.getWriter().write(JSON.toJSONString(result));    }}

自定义授权异常实现

AccessDeniedHandler用来解决认证过的用户访问无权限资源时的异常。

public class CustomizeAccessDeniedHandler implements AccessDeniedHandler {    @Override    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {        CommonResponse result = CommonResponse.error(ResultCode.NO_PERMISSION.getCode(),                        ResultCode.NO_PERMISSION.getMessage());        //处理编码方式,防止中文乱码的情况        response.setContentType("text/json;charset=utf-8");        //塞到HttpServletResponse中返回给前台        response.getWriter().write(JSON.toJSONString(result));    }}

自定义登录成功、失败

AuthenticationSuccessHandler和AuthenticationFailureHandler这两个接口用于登录成功失败以后的处理。

public class CustomizeAuthenticationSuccessHandler implements AuthenticationSuccessHandler {    @Override    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {        AuthUser authUser = (AuthUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();        //返回json数据        CommonResponse<AuthUser> result = CommonResponse.ok(authUser);        //处理编码方式,防止中文乱码的情况        response.setContentType("text/json;charset=utf-8");        //塞到HttpServletResponse中返回给前台        response.getWriter().write(JSON.toJSONString(result));    }}public class CustomizeAuthenticationFailureHandler implements AuthenticationFailureHandler {    @Override    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {        //返回json数据        CommonResponse result = null;        if (exception instanceof AccountExpiredException) {            //账号过期            result = CommonResponse.error(ResultCode.USER_ACCOUNT_EXPIRED.getCode(), ResultCode.USER_ACCOUNT_EXPIRED.getMessage());        } else if (exception instanceof BadCredentialsException) {            //密码错误            result = CommonResponse.error(ResultCode.USER_CREDENTIALS_ERROR.getCode(), ResultCode.USER_CREDENTIALS_ERROR.getMessage());//        } else if (exception instanceof CredentialsExpiredException) {//            //密码过期//            result = CommonResponse.error(ResultCode.USER_CREDENTIALS_EXPIRED);//        } else if (exception instanceof DisabledException) {//            //账号不可用//            result = CommonResponse.error(ResultCode.USER_ACCOUNT_DISABLE);//        } else if (exception instanceof LockedException) {//            //账号锁定//            result = CommonResponse.error(ResultCode.USER_ACCOUNT_LOCKED);//        } else if (exception instanceof InternalAuthenticationServiceException) {//            //用户不存在//            result = CommonResponse.error(ResultCode.USER_ACCOUNT_NOT_EXIST);        } else {            //其他错误            result = CommonResponse.error(ResultCode.COMMON_FAIL.getCode(), ResultCode.COMMON_FAIL.getMessage());        }        //处理编码方式,防止中文乱码的情况        response.setContentType("text/json;charset=utf-8");        //塞到HttpServletResponse中返回给前台        response.getWriter().write(JSON.toJSONString(result));    }}

自定义登出

LogoutHandler自定义登出以后处理逻辑,比如记录在线时长等等;LogoutSuccessHandler登出成功以后逻辑处理。

public class CustomizeLogoutSuccessHandler implements LogoutSuccessHandler {    @Override    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {        CommonResponse result = CommonResponse.ok();        response.setContentType("text/json;charset=utf-8");        response.getWriter().write(JSON.toJSONString(result));    }}public class CustomizeLogoutHandler implements LogoutHandler {    @Override    public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {    }}

自定义认证

自定义认证涉及三个对象UserDetialsService、UserDetails以及PasswordEncoder,整个流程首先根据用户名查询出用户对象交由UserDetialsService接口处理,该接口只有一个方法loadUserByUsername,通过用户名查询用户对象。查询出来的用户对象需要通过Spring Security中的用户数据UserDetails实体类来体现,这里使用AuthUser继承User,User本质上就是继承与UserDetails,UserDetails该类中提供了账号、密码等通用属性。对密码进行校验使用PasswordEncoder组件,负责密码加密与校验。

public class AuthUser extends User {    public AuthUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {        super(username, password, authorities);    }}@Servicepublic class SysUserService implements UserDetailsService {    @Autowired    private SysUserRepository sysUserRepository;    @Autowired    private SysRoleService sysRoleService;    @Autowired    private SysMenuService sysMenuService;    @Override    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {        Optional<SysUser> sysUser = Optional.ofNullable(sysUserRepository.findOptionalByUsername(username).orElseThrow(() ->                new UsernameNotFoundException("未找到用户名")));        List<SysRole> roles = sysRoleService.queryByUserName(sysUser.get().getUsername());        Set<String> dbAuthsSet = new HashSet<>();        if (!CollectionUtils.isEmpty(roles)) {            //角色            roles.forEach(x -> {                dbAuthsSet.add("ROLE_" + x.getName());            });            List<Long> roleIds = roles.stream().map(SysRole::getId).collect(Collectors.toList());            List<SysMenu> menus = sysMenuService.queryByRoleIds(roleIds);            //菜单            Set<String> permissions= menus.stream().filter(x->x.getType().equals(3))                    .map(SysMenu::getPermission).collect(Collectors.toSet());            dbAuthsSet.addAll(permissions);        }        Collection<GrantedAuthority> authorities = AuthorityUtils                .createAuthorityList(dbAuthsSet.toArray(new String[0]));        return new AuthUser(username, sysUser.get().getPassword(), authorities);    }}

基于Token认证

基于Token认证这里我采用JWT方式,下图是整个处理的流程,通过自定义的登录以及JwtAuthenticationTokenFilter来完成整个任务的实现,需要注意的是这里我没有使用缓存

Sping Security前后端分离怎么实现

核心配置

与表单认证不同的是这里关闭和FormLogin表单认证以及不使用Session方式,增加了JwtAuthenticationTokenFilter,此外ResourceAuthExceptionEntryPoint兼职处理之前登录失败以后的异常处理。

@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)@Configurationpublic class SecurityConfig {    @Autowired    private SysUserService sysUserService;    @Autowired    private NotAuthenticationConfig notAuthenticationConfig;    @Bean    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {        //支持跨域        http.cors().and()                //csrf关闭                .csrf().disable()                //不使用session                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)                .and().authorizeRequests(rep -> rep.antMatchers(notAuthenticationConfig.getPermitAllUrls().toArray(new String[0]))                        .permitAll().anyRequest().authenticated())                .exceptionHandling()                //异常认证                .authenticationEntryPoint(new ResourceAuthExceptionEntryPoint())                .accessDeniedHandler(new CustomizeAccessDeniedHandler())                .and()                //token过滤                .addFilterBefore(new JwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)                .userDetailsService(sysUserService);        return http.build();    }        @Bean    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {        return configuration.getAuthenticationManager();    }        @Bean    public PasswordEncoder passwordEncoder() {        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();        return bCryptPasswordEncoder;    }    @Bean("ssc")    public SecuritySecurityCheckService permissionService() {        return new SecuritySecurityCheckService();    }}

Token创建

@Servicepublic class LoginService {    @Autowired    private AuthenticationManager authenticationManager ;    @Autowired    private SysUserService sysUserService;    public LoginVO login(LoginDTO loginDTO) {        //创建Authentication对象        UsernamePasswordAuthenticationToken authenticationToken =                new UsernamePasswordAuthenticationToken(loginDTO.getUsername(),                loginDTO.getPassword());        //调用AuthenticationManager的authenticate方法进行认证        Authentication authentication = authenticationManager.authenticate(authenticationToken);        if(authentication == null) {            throw new RuntimeException("用户名或密码错误");        }        //登录成功以后用户信息、        AuthUser authUser =(AuthUser)authentication.getPrincipal();        LoginVO loginVO=new LoginVO();        loginVO.setUserName(authUser.getUsername());        loginVO.setAccessToken(JwtUtils.createAccessToken(authUser));        loginVO.setRefreshToken(JwtUtils.createRefreshToken(authUser));        return loginVO;    }    public LoginVO refreshToken(String accessToken, String refreshToken){        if (!JwtUtils.validateRefreshToken(refreshToken) && !JwtUtils.validateWithoutExpiration(accessToken)) {            throw new RuntimeException("认证失败");        }        Optional<String> userName = JwtUtils.parseRefreshTokenClaims(refreshToken).map(Claims::getSubject);        if (userName.isPresent()){            AuthUser authUser = sysUserService.loadUserByUsername(userName.get());            if (Objects.nonNull(authUser)) {                LoginVO loginVO=new LoginVO();                loginVO.setUserName(authUser.getUsername());                loginVO.setAccessToken(JwtUtils.createAccessToken(authUser));                loginVO.setRefreshToken(JwtUtils.createRefreshToken(authUser));                return loginVO;            }            throw new InternalAuthenticationServiceException("用户不存在");        }        throw new RuntimeException("认证失败");    }}

Token过滤

public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {        //check Token        if (checkJWTToken(request)) {            //解析token中的认证信息            Optional<Claims> claimsOptional = validateToken(request)                    .filter(claims -> claims.get("authorities") != null);            if (claimsOptional.isPresent()) {                List<String> authoritiesList = castList(claimsOptional.get().get("authorities"), String.class);                List<SimpleGrantedAuthority> authorities = authoritiesList                        .stream().map(String::valueOf)                        .map(SimpleGrantedAuthority::new).collect(Collectors.toList());                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =                        new UsernamePasswordAuthenticationToken(claimsOptional.get().getSubject(), null, authorities);                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);            } else {                SecurityContextHolder.clearContext();            }        }        chain.doFilter(request, response);    }    public static <T> List<T> castList(Object obj, Class<T> clazz) {        List<T> result = new ArrayList<T>();        if (obj instanceof List<?>) {            for (Object o : (List<?>) obj) {                result.add(clazz.cast(o));            }            return result;        }        return null;    }    private Optional<Claims> validateToken(HttpServletRequest req) {        String jwtToken = req.getHeader("token");        try {            return JwtUtils.parseAccessTokenClaims(jwtToken);        } catch (ExpiredJwtException | SignatureException | MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {            //输出日志            return Optional.empty();        }    }    private boolean checkJWTToken(HttpServletRequest request) {        String authenticationHeader = request.getHeader("token");        return authenticationHeader != null;    }}

授权处理

全局授权的配置已经在核心配置中开启,核心思路是通过SecurityContextHolder获取当前用户权限,判断当前用户的权限是否包含该方法的权限,此部分设计后续如果存在性能问题,可以设计缓存来解决。

授权检查

public class SecuritySecurityCheckService {    public boolean hasPermission(String permission) {        return hasAnyPermissions(permission);    }    public boolean hasAnyPermissions(String... permissions) {        if (CollectionUtils.isEmpty(Arrays.asList(permissions))) {            return false;        }        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();        if (authentication == null) {            return false;        }        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();        return authorities.stream().map(GrantedAuthority::getAuthority).filter(x -> !x.contains("ROLE_"))                .anyMatch(x -> PatternMatchUtils.simpleMatch(permissions, x));    }    public boolean hasRole(String role) {        return hasAnyRoles(role);    }    public boolean hasAnyRoles(String... roles) {        if (CollectionUtils.isEmpty(Arrays.asList(roles))) {            return false;        }        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();        if (authentication == null) {            return false;        }        Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();        return authorities.stream().map(GrantedAuthority::getAuthority).filter(x -> x.contains("ROLE_"))                .anyMatch(x -> PatternMatchUtils.simpleMatch(roles, x));    }}

如何使用

@PreAuthorize("@ssc.hasPermission('sys:user:query')")@PostMapping("/helloWord")public String hellWord(){  return "hello word";}

跨域问题处理

关于这部分跨域部分的配置还可以更加细化一点。

@Configurationpublic class CorsConfig implements WebmvcConfigurer {    @Override    public void addCorsMappings(CorsReGIStry registry) {        // 设置允许跨域的路径        registry.addMapping("/**")                // 设置允许跨域请求的域名                .allowedOriginPatterns("*")                // 是否允许cookie                .allowCredentials(true)                // 设置允许的请求方式                .allowedMethods("GET", "POST", "DELETE", "PUT")                // 设置允许的header属性                .allowedHeaders("*")                // 跨域允许时间                .maxAge(3600);    }}

Vue-admin-template登录的简单探索感悟

这部分就是有些感悟(背景自身是没有接触过Vue相关的知识),具体的感悟就是不要畏惧一些自己不知道以及不会的东西,大胆的去尝试,因为自身的潜力是很大的。为什么要这么讲,通过自己折腾3个小时,自己完成整个登录过程,如果是前端可能会比较简单,针对我这种从没接触过的还是有些难度的,需要一些基础配置更改以及流程梳理,这里简单来让大家看下效果,后续我也会自己把剩下菜单动态加载以及一些简单表单交互来完成,做到简单的表单可以自己来实现。

Sping Security前后端分离怎么实现

Sping Security前后端分离怎么实现

到此,相信大家对“Sping Security前后端分离怎么实现”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Sping Security前后端分离怎么实现

本文链接: https://lsjlt.com/news/353452.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Sping Security前后端分离怎么实现
    本篇内容主要讲解“Sping Security前后端分离怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Sping Security前后端分离怎么实现”吧!Spring...
    99+
    2023-07-05
  • JavaScript怎么实现前后端分离
    本篇内容介绍了“JavaScript怎么实现前后端分离”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成! 前...
    99+
    2024-04-02
  • django前后端分离怎么实现
    要实现Django的前后端分离,可以使用Django Rest Framework(DRF)作为后端框架,同时使用一个前端框架(如R...
    99+
    2023-10-09
    django
  • SpringSecurity怎么实现前后端分离
    今天小编给大家分享一下SpringSecurity怎么实现前后端分离的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Sprin...
    99+
    2023-07-05
  • Springboot怎么实现前后端分离excel下载
    本篇内容介绍了“Springboot怎么实现前后端分离excel下载”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Springboot前后端...
    99+
    2023-06-25
  • thinkphp怎么实现前后端分离验证码
    这篇文章主要介绍了thinkphp怎么实现前后端分离验证码的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇thinkphp怎么实现前后端分离验证码文章都会有所收获,下面我们一起来看看吧。一、验证码的作用在互联网时...
    99+
    2023-07-06
  • thinkphp怎么实现前后端分离的开发
    这篇文章主要讲解了“thinkphp怎么实现前后端分离的开发”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“thinkphp怎么实现前后端分离的开发”吧!跨域访问在前后端分离的开发中,前端通常...
    99+
    2023-07-05
  • 基于 Node.js 实现前后端分离
    基本介绍 首先从一个重要的概念“模板”说起。 广义上来说,web中的模板就是填充数据后可以生成文件的页面。 严格意义上来说,应该是模板引擎利用特定格式的文件和所提供的数据编译生成页面。模板大致分为前端模板...
    99+
    2022-06-04
    后端 Node js
  • php前后端分离如何实现
    要实现PHP前后端分离,需要使用以下技术和方法:1. 使用前端框架:选择一个前端框架(如React、Vue.js、Angular等)...
    99+
    2023-10-09
    php
  • SpringSecurity如何实现前后端分离
    这篇文章主要介绍“SpringSecurity如何实现前后端分离”,在日常操作中,相信很多人在SpringSecurity如何实现前后端分离问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringSecur...
    99+
    2023-07-05
  • Spring Security前后分离校验token的实现方法
    目录前言token配置引入JWT依赖文件配置token管理器类security配置配置未登录处理类配置无权限处理类配置登出操作处理类配置token认证过滤器配置token权限校验过滤...
    99+
    2024-04-02
  • git前后端分离怎么用
    随着前端技术的繁荣发展,前端领域出现了越来越多的框架和技术,前后端分离也成为了现阶段 web 开发的一种趋势。其中,git 的使用对于前后端分离的管理起到了至关重要的作用。本文将介绍 git 前后端分离的使用方法。一、前后端分离的基本概念前...
    99+
    2023-10-22
  • thinkPHP5前后端分离
    thinkPHP5前后端分离 环境配置并运行起thinkphp安装小皮面板安装phpstorm配置thinkPHP5将文件放入适当位置修改Nginx配置修改配置和伪静态 跨域前端 ...
    99+
    2023-10-11
    php 前端 nginx 1024程序员节
  • 前后端分离djangorestframe
    关于验证码部分,在我这篇文章里说的挺详细的了:Python高级应用(3)—— 为你的项目添加验证码   这里还是再给一个前后端分离的实例,因为极验官网给的是用session作为验证的,而我们做前后端分离的用的是token,而不是sessi...
    99+
    2023-01-30
    后端 djangorestframe
  • Springboot实现前后端分离excel下载
    目录Springboot前后端分离excel下载前后端分离Excle下载乱码问题前端请求方式 : ajax请求Springboot前后端分离excel下载 现在公司的技术栈是spri...
    99+
    2024-04-02
  • web前后端分离与前后端不分离的区别是什么
    本篇内容主要讲解“web前后端分离与前后端不分离的区别是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“web前后端分离与前后端不分离的区别是什么”吧!前后端...
    99+
    2024-04-02
  • Docker+Nginx打包部署前后端分离怎么实现
    这篇文章主要介绍“Docker+Nginx打包部署前后端分离怎么实现”,在日常操作中,相信很多人在Docker+Nginx打包部署前后端分离怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Docker+...
    99+
    2023-07-04
  • php前后端分离怎么部署
    在进行PHP前后端分离部署的过程中,一般涉及以下几个步骤:1. 前端项目构建:首先,你需要将前端项目构建为静态文件,包括HTML、C...
    99+
    2023-10-20
    php
  • 解析Thinkphp5如何实现前后端分离
    这篇文章主要介绍解析Thinkphp5如何实现前后端分离,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!用Thinkphp5实现纯API开发实现前后端分离大致步骤如下解决跨域请求问题2.改变输出数据格式为API常用返回...
    99+
    2023-06-15
  • FastApi+Vue+LayUI如何实现前后端分离
    小编给大家分享一下FastApi+Vue+LayUI如何实现前后端分离,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!前言在前面的Api开发中,我们使用FastAp...
    99+
    2023-06-25
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作