栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring Security04--前后端分离--JSON传参

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring Security04--前后端分离--JSON传参

上一篇:https://blog.csdn.net/fengxianaa/article/details/124697362

1. 前后端分离

之前功能都是在前后端不分离的情况下,也就是前端的代码在我们后端的项目中

但实际工作中,大多数都是前后端分离的,这样的开发架构下,前后端的交互都是通过 JSON 来进行交互

准备,pom.xml中添加


  com.alibaba
  fastjson
  1.2.60

1. 登陆成功、失败,返回json

修改 SecurityConfig

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/fail.html").permitAll()
        .anyRequest().authenticated()
        .and()
        .formLogin()
        .loginProcessingUrl("/user/login")
        .usernameParameter("name")
        .passwordParameter("pass")
        // 登录成功后设置json格式的返回结果
        .successHandler(new AuthenticationSuccessHandler() {
            
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                                                Authentication authentication) throws IOException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(JSONObject.toJSONString(authentication));
                out.flush();
                out.close();
            }
        })
        // 登录失败后设置json格式的返回结果
        .failureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                                                AuthenticationException exception) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map map = new HashMap<>();
                map.put("errMsg", exception.getMessage());
                out.write(JSONObject.toJSONString(map));
                out.flush();
                out.close();
            }
        })
        .permitAll()
        .and()
        .csrf().disable();//关闭 csrf,后面讨论
    }

用 postman 访问

登陆失败:

2. 未登陆返回json

目前用户没有登陆,访问:localhost:8080/sec,会跳转到登陆页面,

通过下面的设置,让它返回json

.csrf().disable()// 关闭 csrf,后面讨论
    .exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        Map map = new HashMap<>();
        map.put("errMsg", "尚未登录,请先登录");
        out.write(JSONObject.toJSONString(map));
        out.flush();
        out.close();
    }
});

重启后,访问:localhost:8080/sec

3. 退出登录

configure 方法中末尾新增:

http.logout().logoutUrl("/user/logout")
    .logoutSuccessHandler(new LogoutSuccessHandler() {
        @Override
        public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            response.setContentType("application/json;charset=utf-8");
            PrintWriter out = response.getWriter();
            out.write("退出成功");
            out.flush();
            out.close();
        }
    });

重启后,访问:localhost:8080/user/logout

4. json格式登陆

默认情况下是从 UsernamePasswordAuthenticationFilter 的attemptAuthentication方法中获取表单中的参数,

我们新建一个类,继承 UsernamePasswordAuthenticationFilter,复写 attemptAuthentication方法

从json中获取参数

public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {


    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        //如果请求方式不是 post,直接异常
        if (!request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("请求方式有误:" + request.getMethod());
        }
        //如果请求的参数格式不是json,直接异常
        if (!request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){
            throw new AuthenticationServiceException("参数不是json:" + request.getMethod());
        }
        String username=null;
        String password = null;
        try {
            //从 json 数据中 获取用户名、密码
            Map map = JSONObject.parseObject(request.getInputStream(),Map.class);
            username = map.get("username");//但是参数名可能不是这个,最好是用 getUsernameParameter() 方法获取参数名
            password = map.get("password");
        } catch (IOException e) {
            throw new AuthenticationServiceException("参数不对:" + request.getMethod());
        }
        if (username == null) {
            username = "";
        }
        if (password == null) {
            password = "";
        }
        username = username.trim();
        // 封装用户名、密码,下面的 authenticate 方法会从中拿到 用户名, 调用我们的 LoginUserService 获取用户,然后比较密码
        UsernamePasswordAuthenticationToken authRequest
                = new UsernamePasswordAuthenticationToken(username,password);
        //设置ip、sessionId信息
        setDetails(request,authRequest);
        // authenticate 方法中封装了具体的密码认证逻辑
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

修改 SecurityConfig,声明我们的 MyUsernamePasswordAuthenticationFilter 的bean对象

同时使用下面新的 configure 方法,老的可以暂时注释掉

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
        MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
        filter.setAuthenticationManager(authenticationManagerBean());//认证使用
        //设置登陆成功返回值是json
        filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {
            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(JSONObject.toJSONString(authentication));
            }
        });
        //设置登陆失败返回值是json
        filter.setAuthenticationFailureHandler(new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                Map map = new HashMap<>();
                map.put("errMsg", exception.getMessage());
                out.write(JSONObject.toJSONString(map));
                out.flush();
                out.close();
            }
        });
        filter.setFilterProcessesUrl("/user/login");
        return filter;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and().csrf().disable();
        //把自定义认证过滤器加到拦截器链中
        http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

5. 获取当前登录用户

只有一行代码: SecurityContextHolder.getContext().getAuthentication()

修改 SecController

@GetMapping("/sec")
public String sec(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    LoginUser currentUser = (LoginUser) authentication.getPrincipal();
    return JSONObject.toJSONString(currentUser);
}

登录成功后,访问:localhost:8080/sec

更新当前用户信息:SecurityContextHolder.getContext().setAuthentication(authResult)

比如:

// 可以找个地方写个这样的静态方法,如果要更新登录的用户信息,就调用这个方法
public static void setLoginUser(UserDetails userDetails) {
	SecurityContextHolder.getContext().setAuthentication(
		new UsernamePasswordAuthenticationToken(userDetails, userDetails.getPassword(), userDetails.getAuthorities()));
}

6. 验证码

添加验证码生成的工具包


  com.github.penggle
  kaptcha
  2.3.2

修改 SecController 增加:

@GetMapping("/code")
public void getVerifyCode(HttpServletResponse resp, HttpSession session) throws IOException {
    //验证码配置
    Properties properties = new Properties();
    properties.setProperty("kaptcha.image.width", "150");
    properties.setProperty("kaptcha.image.height", "50");
    properties.setProperty("kaptcha.textproducer.char.string", "0123456789");
    properties.setProperty("kaptcha.textproducer.char.length", "4");
    Config config = new Config(properties);
    DefaultKaptcha kaptcha = new DefaultKaptcha();
    kaptcha.setConfig(config);
    
    //生成验证码
    resp.setContentType("image/jpeg");
    String text = kaptcha.createText();
    session.setAttribute("verify_code", text);
    BufferedImage image = kaptcha.createImage(text);
    try(ServletOutputStream out = resp.getOutputStream()) {
        ImageIO.write(image, "jpg", out);
    }
}

修改 SecurityConfig

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/code").permitAll()// 验证码请求不用登录
        .anyRequest().authenticated()
        .and().csrf().disable();

    //把自定义认证过滤器加到拦截器链中
    http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}

修改 MyUsernamePasswordAuthenticationFilter

.....

String username=null;
String password = null;
String code = null;
try {
    Map map = JSONObject.parseObject(request.getInputStream(),Map.class);
    code = map.get("code");
    username = map.get("username");
    password = map.get("password");
} catch (IOException e) {
    throw new AuthenticationServiceException("参数不对:" + request.getMethod());
}
// 校验验证码
String verify_code = (String) request.getSession().getAttribute("verify_code");
if (code == null || verify_code == null || !code.equals(verify_code)) {
    throw new AuthenticationServiceException("验证码错误");
}

.....

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/876951.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号