精华内容
下载资源
问答
  • Springboot前后端分离实现文件上传

    千次阅读 2019-10-25 13:44:08
    Springboot前后端分离实现文件上传 文件上传 文件上传是项目经常要用到的功能,Springboot可以通过配置MultipartFile 来接受文件。 项目采用的是前后端分离的模式,springboot+vue,也可以通过这种方式来实现。 代码...

    Springboot前后端分离实现文件上传

    文件上传

    文件上传是项目经常要用到的功能,Springboot可以通过配置MultipartFile 来接受文件。
    项目采用的是前后端分离的模式,springboot+vue,也可以通过这种方式来实现。

    代码实现

    controller层代码

    实现一
    通过二进制流转化为file

        @ApiOperation(value = "文件上传")
        @PostMapping(value = "/upload")
        @CrossOrigin
        public String fileUpload(
                 MultipartFile file) {
            try {
                byte[] bytes = file.getBytes();
                Path path = Paths.get("C:\\upload\\"+file.getOriginalFilename());
                Files.write(path,bytes);
                return "文件上传成功";
            } catch (IOException e) {
                e.printStackTrace();
                return "文件上传失败";
            }
        }
    

    实现二
    通过MultipartFile 的transferTo方法转成file

        @ApiOperation(value = "文件上传")
        @PostMapping(value = "/upload")
        @CrossOrigin
        public String fileUpload(
                 MultipartFile file) {
            try {
            	String name = file.getOriginalFilename() == null ? "" : file.getOriginalFilename();
            	String path = FileUtil.getTmpPath(Enums.TmpPath.UPLOAD.getCode()) + File.separator + name;
            	File uploadFile = new File(path);
                file.transferTo(uploadFile);
                return "文件上传成功";
            } catch (IOException e) {
                e.printStackTrace();
                return "文件上传失败";
            }
        }
    

    通过PostMan调用刚才写好的文件上传接口
    在这里插入图片描述
    提示文件上传成功。

    展开全文
  • 主要介绍了SpringBoot+Vue前后端分离实现请求api跨域问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 主要介绍了vue+springboot前后端分离实现单点登录跨域问题的解决方法,需要的朋友可以参考下
  • 前后端分离实现Cas单点登录

    千次阅读 热门讨论 2019-09-17 09:47:29
    前后端分离实现单点登录环境介绍会遇到的问题问题解决需要注意的点 环境介绍 前端vue单独部署 后端springboot单独部署 会遇到的问题 跨域 cas认证失败无法重定向,前端302无法捕捉。 问题解决 1、跨域 直接上代码。 ...

    环境介绍

    前端vue单独部署 后端springboot单独部署

    会遇到的问题

    跨域
    cas认证失败无法重定向,前端302无法捕捉。

    问题解决

    1、跨域
    直接上代码。

    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.annotation.Order;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    @EnableWebMvc
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        @Order(0)
        public void addCorsMappings(CorsRegistry registry) {
            //设置允许跨域的路径
            registry.addMapping("/**")
                    //设置允许跨域请求的域名
                    .allowedOrigins("*")
                    //这里:是否允许证书 不再默认开启
                    .allowCredentials(true)
                    //设置允许的方法
                    .allowedMethods("*")
                    //跨域允许时间
                    .maxAge(3600);
        }
        }
    

    2、cas认证失败无法重定向,前端302无法捕捉
    这里就比较复杂了,我的方案就是CAS源码竟然是无法认证直接重定向,而ajax请求又不能直接重定向,导致前端302,而302vue response拦截器是拦截不到的。所以就想到不让cas给我重定向,给我返回状态码,告诉前端认证失败,让前端直接跳转cas服务器登录地址。上代码

    修改cas源码过滤器,复制源码AuthenticationFilter这个过滤器,重写他,其实这里只改了重定向的代码其他都一样。
    在这里插入图片描述
    这个类复制出来把源码复制进去,修改图里面的位置,再把原来使用AuthenticationFilter的地方换成你新的类,这样认证失败就返回的是状态码前端可以拦截。

    这里还是贴上我cas client的一些代码

    这个是我复制源码AuthenticationFilter新建的类

    
    package com.nascent.daren.filter;
    
    import com.alibaba.fastjson.JSON;
    import com.nascent.utils.R;
    import org.jasig.cas.client.Protocol;
    import org.jasig.cas.client.authentication.AuthenticationRedirectStrategy;
    import org.jasig.cas.client.authentication.ContainsPatternUrlPatternMatcherStrategy;
    import org.jasig.cas.client.authentication.DefaultAuthenticationRedirectStrategy;
    import org.jasig.cas.client.authentication.DefaultGatewayResolverImpl;
    import org.jasig.cas.client.authentication.ExactUrlPatternMatcherStrategy;
    import org.jasig.cas.client.authentication.GatewayResolver;
    import org.jasig.cas.client.authentication.RegexUrlPatternMatcherStrategy;
    import org.jasig.cas.client.authentication.UrlPatternMatcherStrategy;
    import org.jasig.cas.client.configuration.ConfigurationKeys;
    import org.jasig.cas.client.util.AbstractCasFilter;
    import org.jasig.cas.client.util.CommonUtils;
    import org.jasig.cas.client.util.ReflectUtils;
    import org.jasig.cas.client.validation.Assertion;
    
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.HashMap;
    import java.util.Map;
    
    public class DaRenAuthenticationFilter extends AbstractCasFilter {
        /**
         * The URL to the CAS Server login.
         */
        private String casServerLoginUrl;
    
        /**
         * Whether to send the renew request or not.
         */
        private boolean renew = false;
    
        /**
         * Whether to send the gateway request or not.
         */
        private boolean gateway = false;
    
        private GatewayResolver gatewayStorage = new DefaultGatewayResolverImpl();
    
        private AuthenticationRedirectStrategy authenticationRedirectStrategy = new DefaultAuthenticationRedirectStrategy();
    
        private UrlPatternMatcherStrategy ignoreUrlPatternMatcherStrategyClass = null;
    
        private static final Map<String, Class<? extends UrlPatternMatcherStrategy>> PATTERN_MATCHER_TYPES =
                new HashMap<String, Class<? extends UrlPatternMatcherStrategy>>();
    
        static {
            PATTERN_MATCHER_TYPES.put("CONTAINS", ContainsPatternUrlPatternMatcherStrategy.class);
            PATTERN_MATCHER_TYPES.put("REGEX", RegexUrlPatternMatcherStrategy.class);
            PATTERN_MATCHER_TYPES.put("EXACT", ExactUrlPatternMatcherStrategy.class);
        }
    
        public DaRenAuthenticationFilter() {
            this(Protocol.CAS2);
        }
    
        protected DaRenAuthenticationFilter(final Protocol protocol) {
            super(protocol);
        }
    
        protected void initInternal(final FilterConfig filterConfig) throws ServletException {
            if (!isIgnoreInitConfiguration()) {
                super.initInternal(filterConfig);
                setCasServerLoginUrl(getString(ConfigurationKeys.CAS_SERVER_LOGIN_URL));
                setRenew(getBoolean(ConfigurationKeys.RENEW));
                setGateway(getBoolean(ConfigurationKeys.GATEWAY));
    
                final String ignorePattern = getString(ConfigurationKeys.IGNORE_PATTERN);
                final String ignoreUrlPatternType = getString(ConfigurationKeys.IGNORE_URL_PATTERN_TYPE);
    
                if (ignorePattern != null) {
                    final Class<? extends UrlPatternMatcherStrategy> ignoreUrlMatcherClass = PATTERN_MATCHER_TYPES.get(ignoreUrlPatternType);
                    if (ignoreUrlMatcherClass != null) {
                        this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlMatcherClass.getName());
                    } else {
                        try {
                            logger.trace("Assuming {} is a qualified class name...", ignoreUrlPatternType);
                            this.ignoreUrlPatternMatcherStrategyClass = ReflectUtils.newInstance(ignoreUrlPatternType);
                        } catch (final IllegalArgumentException e) {
                            logger.error("Could not instantiate class [{}]", ignoreUrlPatternType, e);
                        }
                    }
                    if (this.ignoreUrlPatternMatcherStrategyClass != null) {
                        this.ignoreUrlPatternMatcherStrategyClass.setPattern(ignorePattern);
                    }
                }
    
                final Class<? extends GatewayResolver> gatewayStorageClass = getClass(ConfigurationKeys.GATEWAY_STORAGE_CLASS);
    
                if (gatewayStorageClass != null) {
                    setGatewayStorage(ReflectUtils.newInstance(gatewayStorageClass));
                }
    
                final Class<? extends AuthenticationRedirectStrategy> authenticationRedirectStrategyClass = getClass(ConfigurationKeys.AUTHENTICATION_REDIRECT_STRATEGY_CLASS);
    
                if (authenticationRedirectStrategyClass != null) {
                    this.authenticationRedirectStrategy = ReflectUtils.newInstance(authenticationRedirectStrategyClass);
                }
            }
        }
    
        public void init() {
            super.init();
            CommonUtils.assertNotNull(this.casServerLoginUrl, "casServerLoginUrl cannot be null.");
        }
    
        public final void doFilter(final ServletRequest servletRequest, final ServletResponse servletResponse,
                                   final FilterChain filterChain) throws IOException, ServletException {
    
            final HttpServletRequest request = (HttpServletRequest) servletRequest;
            final HttpServletResponse response = (HttpServletResponse) servletResponse;
    
            if (isRequestUrlExcluded(request)) {
                logger.debug("Request is ignored.");
                filterChain.doFilter(request, response);
                return;
            }
    
            final HttpSession session = request.getSession(false);
            final Assertion assertion = session != null ? (Assertion) session.getAttribute(CONST_CAS_ASSERTION) : null;
    
            if (assertion != null) {
                filterChain.doFilter(request, response);
                return;
            }
    
            final String serviceUrl = constructServiceUrl(request, response);
            final String ticket = retrieveTicketFromRequest(request);
            final boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
    
            if (CommonUtils.isNotBlank(ticket) || wasGatewayed) {
                filterChain.doFilter(request, response);
                return;
            }
    
            final String modifiedServiceUrl;
    
            logger.debug("no ticket and no assertion found");
            if (this.gateway) {
                logger.debug("setting gateway attribute in session");
                modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
            } else {
                modifiedServiceUrl = serviceUrl;
            }
    
            logger.debug("Constructed service url: {}", modifiedServiceUrl);
    
            final String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl,
                    getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
    
            logger.debug("redirecting to \"{}\"", urlToRedirectTo);
            PrintWriter out = response.getWriter();
    
            response.setContentType("application/json; charset=UTF-8");
            out.println(JSON.toJSONString(R.error(401,"登陆出错")));
            //this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
        }
    
        public final void setRenew(final boolean renew) {
            this.renew = renew;
        }
    
        public final void setGateway(final boolean gateway) {
            this.gateway = gateway;
        }
    
        public final void setCasServerLoginUrl(final String casServerLoginUrl) {
            this.casServerLoginUrl = casServerLoginUrl;
        }
    
        public final void setGatewayStorage(final GatewayResolver gatewayStorage) {
            this.gatewayStorage = gatewayStorage;
        }
    
        private boolean isRequestUrlExcluded(final HttpServletRequest request) {
            if (this.ignoreUrlPatternMatcherStrategyClass == null) {
                return false;
            }
    
            final StringBuffer urlBuffer = request.getRequestURL();
            if (request.getQueryString() != null) {
                urlBuffer.append("?").append(request.getQueryString());
            }
            final String requestUri = urlBuffer.toString();
            return this.ignoreUrlPatternMatcherStrategyClass.matches(requestUri);
        }
    }
    

    这个是springboot配置的cas类

    import org.jasig.cas.client.session.SingleSignOutFilter;
    import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
    import org.jasig.cas.client.util.AssertionThreadLocalFilter;
    import org.jasig.cas.client.util.HttpServletRequestWrapperFilter;
    import org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.web.authentication.logout.LogoutFilter;
    import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    @Configuration
    @Component
    public class CasConfigure {
        /**
         * cas服务端地址
         */
       // private String  casServerLoginUrl="";
        private String  casServerLoginUrl="";
        /**lo
         * 当前应用地址
         */
        @Value("${env.serverName}")
        private String serverName;
    
    
    
    
        /**
         * 该监听器用于实现单点登出功能,session失效监听器
         */
        @Bean
        public ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> singleSignOutHttpSessionListener() {
            ServletListenerRegistrationBean<SingleSignOutHttpSessionListener> listener = new ServletListenerRegistrationBean<>();
            listener.setEnabled(true);
            listener.setListener(new SingleSignOutHttpSessionListener());
            listener.setOrder(1);
            return listener;
        }
    
        /**
         * 该过滤器用于实现单点登出功能,单点退出配置,一定要放在其他filter之前
         *  当调用当前应用的/logout时,该拉截器将会重定向到cas服务端的/logout请求
         */
        @Bean
        public FilterRegistrationBean logOutFilter() {
            FilterRegistrationBean filterRegistration = new FilterRegistrationBean();//new SecurityContextLogoutHandler()
            LogoutFilter logoutFilter = new LogoutFilter(casServerLoginUrl + "/logout?service=" + serverName,new SecurityContextLogoutHandler());
            filterRegistration.setFilter(logoutFilter);
            filterRegistration.setEnabled(true);
            filterRegistration.addUrlPatterns("/logout");
            filterRegistration.addInitParameter("casServerUrlPrefix", casServerLoginUrl);
            filterRegistration.addInitParameter("serverName", serverName);
            filterRegistration.setOrder(2);
            return filterRegistration;
        }
    
    
        /**
         * 该过滤器用于实现单点登出功能,当一个系统登出时,cas服务端会通知,各个应
         * 用进行进行退出操作,该过滤器就是用来接收cas回调的请求,如果是前后端分离
         * 应用,需要重写SingleSignOutFilter过滤器,按自已的业务规则去处理
         */
        @Bean
        public FilterRegistrationBean singleSignOutFilter() {
            FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
            filterRegistration.setFilter(new SingleSignOutFilter());
            filterRegistration.setEnabled(true);
            filterRegistration.addUrlPatterns("/*");
            filterRegistration.addInitParameter("casServerUrlPrefix", casServerLoginUrl);
            filterRegistration.addInitParameter("serverName", serverName);
            filterRegistration.setOrder(3);
            return filterRegistration;
        }
    
        /**
         * 该过滤器负责单点登录功能,用户登录的认证工作
         * @return
         */
        @Bean
        public FilterRegistrationBean authenticationFilterRegistrationBean() {
            FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
            authenticationFilter.setFilter(new DaRenAuthenticationFilter()); //这里就是被换的类
            Map<String, String> initParameters = new HashMap<String, String>();
            initParameters.put("casServerLoginUrl", casServerLoginUrl);
           initParameters.put("ignorePattern", "/api/");
            initParameters.put("serverName",serverName);
            authenticationFilter.setInitParameters(initParameters);
            authenticationFilter.setOrder(4);
            List<String> urlPatterns = new ArrayList<String>();
            urlPatterns.add("/*");
            authenticationFilter.setUrlPatterns(urlPatterns);
            return authenticationFilter;
        }
    
        /**
         * 该过滤器用于单点登录功能,负责对Ticket的校验工作
         * @return
         */
        @Bean
        public FilterRegistrationBean ValidationFilterRegistrationBean(){
            FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
            authenticationFilter.setOrder(5);
            authenticationFilter.setFilter(new Cas20ProxyReceivingTicketValidationFilter());
    
            Map<String, String> initParameters = new HashMap<>();
            initParameters.put("casServerUrlPrefix", casServerLoginUrl);
            initParameters.put("serverName", serverName);
            authenticationFilter.setInitParameters(initParameters);
    
            List<String> urlPatterns = new ArrayList<String>();
            urlPatterns.add("/*");
            authenticationFilter.setUrlPatterns(urlPatterns);
            return authenticationFilter;
        }
    
    
        /**
         * 该过滤器用于单点登录功能 ,对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名
         * @return
         */
    
        @Bean
        public FilterRegistrationBean casHttpServletRequestWrapperFilter(){
            FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
            authenticationFilter.setFilter(new HttpServletRequestWrapperFilter());
            authenticationFilter.setOrder(6);
            List<String> urlPatterns = new ArrayList<String>();
            urlPatterns.add("/*");
            authenticationFilter.setUrlPatterns(urlPatterns);
            return authenticationFilter;
        }
    
        /**
         * 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。
         比如AssertionHolder.getAssertion().getPrincipal().getName()。
         这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息
         * @return
         */
        @Bean
        public FilterRegistrationBean casAssertionThreadLocalFilter(){
            FilterRegistrationBean authenticationFilter = new FilterRegistrationBean();
            authenticationFilter.setFilter(new AssertionThreadLocalFilter());
            authenticationFilter.setOrder(7);
            List<String> urlPatterns = new ArrayList<>();
            urlPatterns.add("/*");
            authenticationFilter.setUrlPatterns(urlPatterns);
            return authenticationFilter;
        }
    }
    

    到这里前端就可以捕捉跳转cas服务器的登录地址。
    这里一定要注意前端跳转指定Cas回调地址必须是后端地址,因为cas服务器返回的票据还需要后端验证。这样session cookie才会正确。那么后台还得提供一个ValidateController里面在跳转前端服务器地址。
    上代码
    前端拦截器

    axiosInstance.interceptors.response.use(response => {
        let status = response.data.code;
        let url =  "http://cas服务登录地址/?service=回调后端控制器/daren/checkToken";
        if(status ===401){
            window.location.href = url;
            return
        }
        }
    

    后台控制器

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    
    @Controller
    @RequestMapping("/daren")
    public class ValidateController {
        @RequestMapping("/checkToken")
        public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
            response.sendRedirect(”前端服务器地址");//
        }
    }
    

    好了整个流程就可以走通了。

    需要注意的点

    1、所有前后端地址,如果是本地调试,需要统一,都是用ip那么地址都配置成ip,是localhost就都是localhost
    2、重要点还是在改掉原来的cas重定向的逻辑。理解了就知道怎么弄了。

    展开全文
  • Springboot前后端分离实现CAS单点登录

    千次阅读 热门讨论 2020-03-20 19:40:14
    springboot前后端分离实现cas单点登录1.客户端构建1.1pom依赖1.2yml配置1.3后端代码1.4页面2.问题记录2.1在前后端分离情况下,AuthenticationFilter重定向问题,导致前端发生跨域2.2AuthenticationFilter自定义...

    1.CAS服务端构建

    1.1war包部署

    cas5.3版本
    https://github.com/apereo/cas-overlay-template
    构建完成后将war包部署到tomcat即可
    在这里插入图片描述

    1.2配置文件修改

    支持http协议
    修改apache-tomcat-8.5.53\webapps\cas\WEB-INF\classes\services目录下的HTTPSandIMAPS-10000001.json,在serviceId中添加http即可

    {
      "@class" : "org.apereo.cas.services.RegexRegisteredService",
      "serviceId" : "^(https|http|imaps)://.*",
      "name" : "HTTPS and IMAPS",
      "id" : 10000001,
      "description" : "This service definition authorizes all application urls that support HTTPS and IMAPS protocols.",
      "evaluationOrder" : 10000
    }
    

    apache-tomcat-8.5.53\webapps\cas\WEB-INF\classesapplication.properties添加配置

    cas.tgc.secure=false
    cas.serviceRegistry.initFromJson=true
    

    配置默认登录用户名密码及登出重定向
    修改apache-tomcat-8.5.53\webapps\cas\WEB-INF\classesapplication.properties配置

    cas.authn.accept.users=admin::admin
    
    #配置允许登出后跳转到指定页面
    cas.logout.followServiceRedirects=true
    

    1.3启动

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

    1.客户端构建

    1.1pom依赖

    <dependency>
        <groupId>net.unicon.cas</groupId>
        <artifactId>cas-client-autoconfig-support</artifactId>
        <version>2.3.0-GA</version>
    </dependency>
    

    1.2yml配置

    client-host-url配置的地址和前端ajax调用的地址必须一致,统一使用ip:port或hostname:port;如果本地后端配置localhost,前端使用ip,会造成Ticket验证失败

    cas:
      server-url-prefix: http://172.19.25.113:8080/cas
      server-login-url: http://172.19.25.113:8080/cas/login
      client-host-url: http://172.19.25.113:1010
      validation-type: cas
      use-session: true
      authentication-url-patterns:
        /auth
    

    1.3后端代码

    启动类添加@EnableCasClient注解

    @EnableCasClient
    @SpringBootApplication
    public class SpringbootCasDemoApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringbootCasDemoApplication.class, args);
        }
    }
    

    自定义AuthenticationFilter重定向策略

    public class CustomAuthRedirectStrategy implements AuthenticationRedirectStrategy {
    
        @Override
        public void redirect(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s) throws IOException {
            httpServletResponse.setCharacterEncoding("utf-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.write("401");
        }
    }
    

    Cors及CasClient相关filter初始化参数配置

    @Configuration
    public class CasAuthConfig extends CasClientConfigurerAdapter {
    
        @Override
        public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
            Map<String, String> initParameters = authenticationFilter.getInitParameters();
            initParameters.put("authenticationRedirectStrategyClass", "cc.jasonwang.springbootcasdemo.config.CustomAuthRedirectStrategy");
        }
    
        @Override
        public void configureValidationFilter(FilterRegistrationBean validationFilter) {
            Map<String, String> initParameters = validationFilter.getInitParameters();
            initParameters.put("encodeServiceUrl", "false");
        }
    
        @Bean
        public FilterRegistrationBean corsFilter() {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            CorsConfiguration config = new CorsConfiguration();
            config.setAllowCredentials(true);
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");
            config.addAllowedMethod("*");
            source.registerCorsConfiguration("/**", config);
            FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
            registrationBean.setFilter(new CorsFilter(source));
            registrationBean.setOrder(-2147483648);
            return registrationBean;
        }
    }
    

    Controller

    @RestController
    public class HelloController {
    
        @Value("${cas.server-url-prefix}")
        private String casServerUrlPrefix;
    
        @GetMapping("/auth")
        public void auth(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
            Assertion assertion = (Assertion) session.getAttribute("_const_cas_assertion_");
            response.setHeader("Content-type", "application/json;charset=UTF-8");
            response.setCharacterEncoding("utf-8");
            response.setStatus(200);
            if (assertion != null) {
                String redirectUrl= request.getParameter("redirectUrl");
                try {
                    response.setHeader("Content-type", "text/html;charset=UTF-8");
                    response.sendRedirect(redirectUrl);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    response.getWriter().print("401");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @GetMapping("/logout")
        public RedirectView logout(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
            session.invalidate();
            String indexPageUrl = "http://127.0.0.1";
            return new RedirectView( casServerUrlPrefix + "/logout?service=" + indexPageUrl);
        }
    }
    

    1.4页面

    <!DOCTYPE html>
    <html lang="en" dir="ltr">
      <head>
        <meta charset="utf-8">
        <title></title>
      </head>
      <body>
          <span>单点地址:</span><input class="url" type="text"/><br>
          <button type="button" class="button">登录</button><br>
          <div class="response" style="width: 200px;height:200px;border: 1px solid #3333;"></div>
         <script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
          <script type="text/javascript">
            $(".button").click(function(){
              $.get("http://172.19.25.113:1010/auth", function(data){
                $(".response").text(data)
                if(data == 401){
                  window.location.href = "http://localhost:8080/cas/login?service=http://172.19.25.113:1010/auth?redirectUrl=http://127.0.0.1"
                }
              })
            })
          </script>
      </body>
    </html>
    

    在这里插入图片描述
    在这里插入图片描述
    这里只是验证前后端分离下页面url跳转问题,页面没有放在nginx服务上
    在这里插入图片描述

    2.问题记录

    2.1在前后端分离情况下,AuthenticationFilter重定向问题,导致前端发生跨域

    一个关于单点登录框架CAS在前后端分离下的解决方案

    (1)描述
    cas前后端不分离的情况下是能够直接跳转的,然而前后端分离后,前端ajax访问后端在经过AuthenticationFilter时,验证未登录会重定向到CAS登录,导致前端发生跨域问题

    (2)解决思路
    在AuthenticationFilter中不进行重定向,验证未登录就直接返回一个错误状态码;由前端获取到状态码后进行判断,再跳转到CAS登录地址
    AuthenticationFilter

    public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        if (this.isRequestUrlExcluded(request)) {
            this.logger.debug("Request is ignored.");
            filterChain.doFilter(request, response);
        } else {
        	// 获取Assertion 验证是否登录
            HttpSession session = request.getSession(false);
            Assertion assertion = session != null ? (Assertion)session.getAttribute("_const_cas_assertion_") : null;
            if (assertion != null) {
                filterChain.doFilter(request, response);
            } else {
                String serviceUrl = this.constructServiceUrl(request, response);
                String ticket = this.retrieveTicketFromRequest(request);
                boolean wasGatewayed = this.gateway && this.gatewayStorage.hasGatewayedAlready(request, serviceUrl);
                if (!CommonUtils.isNotBlank(ticket) && !wasGatewayed) {
                    this.logger.debug("no ticket and no assertion found");
                    String modifiedServiceUrl;
                    if (this.gateway) {
                        this.logger.debug("setting gateway attribute in session");
                        modifiedServiceUrl = this.gatewayStorage.storeGatewayInformation(request, serviceUrl);
                    } else {
                        modifiedServiceUrl = serviceUrl;
                    }
    
                    this.logger.debug("Constructed service url: {}", modifiedServiceUrl);
                    String urlToRedirectTo = CommonUtils.constructRedirectUrl(this.casServerLoginUrl, this.getProtocol().getServiceParameterName(), modifiedServiceUrl, this.renew, this.gateway);
                    this.logger.debug("redirecting to \"{}\"", urlToRedirectTo);
                    // 通过这个方法进行重定向
                    this.authenticationRedirectStrategy.redirect(request, response, urlToRedirectTo);
                } else {
                    filterChain.doFilter(request, response);
                }
            }
        }
    }
    

    DefaultAuthenticationRedirectStrategy

    public final class DefaultAuthenticationRedirectStrategy implements AuthenticationRedirectStrategy {
        public DefaultAuthenticationRedirectStrategy() {
        }
    
        public void redirect(HttpServletRequest request, HttpServletResponse response, String potentialRedirectUrl) throws IOException {
        	//response重定向
            response.sendRedirect(potentialRedirectUrl);
        }
    }
    

    (3)实现
    自定义重定向策略,将DefaultAuthenticationRedirectStrategy替换掉
    CustomAuthRedirectStrategy

    public class CustomAuthRedirectStrategy implements AuthenticationRedirectStrategy {
        @Override
        public void redirect(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, String s) throws IOException {
            httpServletResponse.setCharacterEncoding("utf-8");
            httpServletResponse.setContentType("application/json; charset=utf-8");
            PrintWriter out = httpServletResponse.getWriter();
            out.write("401");
        }
    }
    
    @Configuration
    public class CasAuthConfig extends CasClientConfigurerAdapter {
    
        @Override
        public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
            Map<String, String> initParameters = authenticationFilter.getInitParameters();
            initParameters.put("authenticationRedirectStrategyClass", "cc.jasonwang.springbootcasdemo.config.CustomAuthRedirectStrategy");
    	}
    }
    

    2.2AuthenticationFilter自定义重定向策略实现后,前端仍然发生跨域问题

    Spring 里那么多种 CORS 的配置方式,到底有什么区别

    (1)描述
    原使用WebMvcConfigurationSupport实现CORS,AuthenticationFilter输出状态码后,前端仍然发生跨域问题

    @Configuration
    public class CorsConfig extends WebMvcConfigurationSupport {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedHeaders("*")
                    .allowedMethods("*")
                    .maxAge(3600)
                    .allowCredentials(true);
        }
    }
    

    (2)解决思路
    通过查找资料发现:

    • 实现 WebMvcConfigurationSupport .addCorsMappings 方法来进行的 CORS 配置,最后会在 Spring 的 Interceptor 或 Handler 中生效
    • 注入 CorsFilter 的方式会让 CORS 验证在 Filter 中生效
      在这里插入图片描述

    (3)实现
    修改CORS实现方式

    @Bean
    public FilterRegistrationBean corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new CorsFilter(source));
        registrationBean.setOrder(-2147483648);
        return registrationBean;
    }
    

    2.3前端跳转CAS登录并传递redirectUrl参数,Ticket票据验证问题

    (1)原因
    Cas20ProxyReceivingTicketValidationFilter在进行Ticket验证时,CAS重定向的service地址进行了URLEncoder编码,而CAS使用Ticket获取到存储的service地址未进行编码,导致两个service不一致,造成Ticket票据验证失败
    在这里插入图片描述
    (2)debug定位问题

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

    找到CAS服务器接口地址后,便想到在CAS服务器端看下接口是怎么实现的,下面就是在CAS服务器debug后的结果

    CAS Server
    在web.xml中找到了servlet映射
    在这里插入图片描述
    定位到SafeDispatcherServlet,根据目录结构和类文件名称找到了ServiceValidateController
    在这里插入图片描述

    ServiceValidateController
    在这里插入图片描述
    CentralAuthenticationServiceImpl
    在这里插入图片描述
    ServiceTicketImpl
    在这里插入图片描述
    AbstractWebApplicationService
    在这里插入图片描述
    (3)实现
    对Cas20ProxyReceivingTicketValidationFilter添加encodeServiceUrl=false初始化参数

    @Configuration
    public class CasAuthConfig extends CasClientConfigurerAdapter {
    
        @Override
        public void configureAuthenticationFilter(FilterRegistrationBean authenticationFilter) {
            Map<String, String> initParameters = authenticationFilter.getInitParameters();
            initParameters.put("authenticationRedirectStrategyClass", "cc.jasonwang.springbootcasdemo.config.CustomAuthRedirectStrategy");
        }
    
        @Override
        public void configureValidationFilter(FilterRegistrationBean validationFilter) {
            Map<String, String> initParameters = validationFilter.getInitParameters();
            initParameters.put("encodeServiceUrl", "false");
        }
    }
    

    3.参考

    展开全文
  • 前后端分离实现验证码

    千次阅读 2020-08-13 20:38:07
    问题: 如何实现前后端分离时验证码问题

    问题:

    如何实现前后端分离时验证码问题。因为cookie、session因为前后分离时是跨域的,因此无法使用。(准确的说我不知道怎么搞=_=)所有验证码数据不能像原始的开发方式那样存储在session中。因此,需要存储在redis这样的缓存中且设置过期时间,对应的key返回到前端,原理类似sessionId。(不建议存在数据库)

    解决

    1、 前端:img标签中将图片经过BASE64编码的字符串设置在src=“data:image/gif;base64,xxxbase64字符串xxx”,这样可以显示图片。
    2、后端接口:将以json格式返回{captchaKey: “xxxUUIDxxx”,image:“xxxbase64字符串xxx”}。

    示意代码:

    @CrossOrigin
        @GetMapping("/captcha")
        public Map<String,Object> captcha() throws Exception {
    
            VCodeUtil vc = new VCodeUtil();
            // 字节数组输出流,将其转变为byte[] 后再Base64编码传输到前端
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            vc.saveImage(vc.getImage(),byteOut);
            String vcStr = vc.getText();
    
            // 将captcha值存入redis,图片放入响应中
            // key ? how to get?
            String captchakey = CommonTool.getUUIDStr();
            System.out.println(captchakey);
            // setCaptchaToRedis("captchakey",vcStr)
            // 只能将图片变为base64编码的字符串才能以json响应传回
            String stringImg = "data:image/gif;base64,"+Base64.getEncoder().encodeToString(byteOut.toByteArray());
    
            Map<String,Object> map = new HashMap<>();
            map.put("captchakey",captchakey);
            map.put("image",stringImg);
            return map;
        }
    
    

    前端效果和代码:
    在这里插入图片描述

    <body>
    	<h2>GET/POST数据到后台:</h2>
    	<button class="captcha">captcha</button>
    	<br>
    	<img src="data:image/gif;base64,R0lGODlheAAoAPIAAGAcEgAA/zrMpI8cIv6I8P///2AcEmAcEiwAAAAAeAAoAEII/wALCBxIsKDBgwgTKlzIsKHDhxAjFhBAUYDEiw8DNAygESKBjwQMggwJsSLGhABSFkwJICFHjQNiypwZsyDHhiNJFsgJsiTFkytVAh1Ic8BLlx0X8lzq8+dQgSyjCo1YU+LNpw0rOsXKtevBpT0lavXq8CjQpAxzdjXJlSXCqQzNFhxgEG3CkV/DOmT71K1Bvw6rIrxK0C7Zw4i7Gk7MuLFjsXwfxy0rmeHYymUX29R8V+9OvE0tkpUKuDBhqw9BF1SddSvW0gNh1+UomCDdzWlZD40MVDbGmkdvD1aqW2BxhbxP+vadsHYBunJNE/+4+njCy0NlM28uHCJnkTqtI+bE3hYuxKJWv2Nej1g9+/fwL2oVHb/+0/n2bVLOPzF5/dNI8UcefwAOhxFT8vnHWHQHuVfgdNR95tleCmYn1WzuKfQgQmoRJN54FWIEWFQNbhhXhhLqNFCHFNL3mnlQtYRUR849151AGbJ4n2sWyviXjxrShtCNJq74YUQhRrTdeUYldaN0CrEIVlPlAQmUYEIG2NmExnGJHI/KwfhbdzGZqN5xRxaU5EPLicndXAMVWKSOKapo2ZoNtUnVkydGqRpPob0I15JD8tmnn2BF2KKLPZJ4UY378ScpRkVOaulgKF6qKUEBAQA7" id="verImg">
    	
    
    <!--头尾,脚本分界线-->
    <script>
    var postdata={
    	name : "demographics",
    	params : [1 , 2, 3],
    	items : [
    		{name : "中文", value : 10 },
    		{name : "数学",  value : 20 }
    	]
    };
    
    $(function(){
    	$(".captcha").click(function(){
    		$.ajax({
    	        //contentType : 'application/json;charset=utf-8',
    			url : 'http://localhost:8082/fleamarkt/captcha',
    	        type : 'GET',
    	        data : {
    				
    			},
    	      	success: function(res, textStatus) {
    				console.log(res)
    				$('#verImg').attr('src', res.image);
    			},
    			error: function() {
    				alert("访问失败!");
    			}
    			
         	});
    	});
    	
    });
    </script>
    
    </body>
    
    

    参考

    登录验证码(前后端分离.不分离)

    展开全文
  • 前后端分离实现上传图片的功能

    千次阅读 2020-01-09 17:59:00
    前后端分离实现上传图片的功能 思路 前端上传图片和分类名称,保存图片文件,将数据追加到formData传给后端,回调清空。 后端对图片和分类名分开进行处理。分类名进行普通的增加。图片需要借助工具类转化成统一格式...
  • java前后端分离实现方式

    千次阅读 2018-11-28 14:57:56
    因为公司需要前端跟后端要进行分离,分离产生最关键的问题就是跨域。本文描述的是使用nginx进行前后端分离。话不多说,直接上nginx配置文件一份。 ...
  • 原文地址: luoliangDSGA’s blog ... 欢迎转载,转载请注明作者及出处,谢谢!... 之前写过一篇SpringBoot+Vue前后端分离实现文件上传的博客,但是那篇博客主要针对的是小文件的上传,如果是大文件,一次性上传...
  • springboot+shiro+redis前后端分离实现认证(一) 一、shiro架构图与基本知识 四大功能: (1)认证 (2)授权 (3)加密 (4)会话管理 1.1 Subject Subject 即主题,外部应用与subject进行交互,subject...
  • 微信第三方登录前后端分离实现思路 前端实现 这里说一下前后端的思路,页面加载时声明一个变量state=‘时间戳+6位随机数’, 前端路径生成二维码, 其中有个state参数需要我们传递,这个参数你传什么,微信回调的...
  • Spring Boot 2.0 WebSocket Vue 前后端分离实现消息通讯一、前言二、快速开始三、注意事项讲一下过程中踩得坑 一、前言 目前项目中要实现一个消息推送的功能,类似于空间点赞,朋友圈新消息的提醒功能,然后马上想到...
  • 原文来自 © 呆萌钟 vue+springboot前后端分离实现单点登录跨域问题处理 最近在做一个后台管理系统,前端是用时下火热的vue.js,后台是基于springboot的。因为后台系统没有登录功能,但是公司要求统一登录,登录...
  • 接上文SpringBoot前后端分离实现拼图滑动验证码(后端) 最终效果 主要结构 没做可插拔处理,直接在html里写标签了。主要包括包含了背景和拼图的模块slide_img_content,包含滑动条的模块slider。其中slider_mark...
  • <!-- 根据用户id查询菜单列表 --> <select id="getMenuByAdminId" resultMap="Menus"> SELECT DISTINCT m1.*, m2.id AS id2, m2.url AS url2, m2.path AS path2, ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,282
精华内容 26,912
关键字:

前后端分离怎么实现