springSecurity (四)实现前后端json交互

一.搭建项目

和第二节配置一样

二.代码实例

注意:与三相比关键是过滤器链写法不同

2.1 SecurityConfig 配置(关键)

注意:

1.用户名或者密码单独报错的异常是不会进入过滤器链的即if (e instanceof BadCredentialsException) 和else if (e instanceof UsernameNotFoundException) ,默认情况下,这个分支是不会进来的,Spring Security 自动隐藏了了这个异常,如果系统中发生了 UsernameNotFoundException 会被自动转为 BadCredentialsException 异常然后抛出来(就是我们在二中提到的默认隐藏异常)
2.csrf().disable() 关闭 csrf 防御机制,这个 disable 方法本质上就是从 Spring Security 的过滤器链上移除掉 csrf 过滤器(否则我们这里访问不到页面的)

    package com.huang.springsecurity.comments.config;

    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.huang.springsecurity.comments.result.RespBean;
    import com.huang.springsecurity.model.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.authentication.*;
    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.WebSecurityCustomizer;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.web.SecurityFilterChain;

    import java.io.PrintWriter;

    @Configuration
    public class SecurityConfig {
        @Bean
        WebSecurityCustomizer securityCustomizer() {
            return new WebSecurityCustomizer() {
                @Override
                public void customize(WebSecurity web) {
                    web.ignoring().antMatchers("/login.html");
                }
            };
        }

        /**
         * 自己手动配置安全过滤器链
         *
         * @return
         */
        @Bean
        SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
            //开始认证
            http.authorizeRequests()
                    //请求路径如果是 /login.html,则这个请求可以匿名通过
                    .antMatchers("/login.html").anonymous()
                    //所有的请求,类似于 shiro 中的 /**
                    .anyRequest()
                    //必须要认证之后才能访问,类似于 shiro 中的 authc
                    .authenticated()
                    .and()
                    //开始配置登录表单
                    .formLogin()
                    //配置登录页面,如果访问了一个需要认证之后才能访问的页面,那么就会自动跳转到这个页面上来
                    .loginPage("/login.html")
                    //配置处理登录请求的接口,本质上其实就是配置过滤器的拦截规则,将来的登录请求就会在过滤器中被处理
                    .loginProcessingUrl("/doLogin")
                    //配置登录表单中用户名的 key
                    .usernameParameter("username")
                    //配置登录表单中用户密码
                    .passwordParameter("password")
                    //配置登录成功后的跳转地址
//                .defaultSuccessUrl("/hello")
//                .failureUrl("/login.html")
                    //登录成功处理器
                    //req:当前请求对象
                    //resp:当前响应对象
                    //auth:当前认证成功的用户信息
                    .successHandler((req, resp, auth) -> {
                        resp.setContentType("application/json;charset=utf-8");
                        User principal = (User) auth.getPrincipal();
                        principal.setPassword(null);
                        RespBean respBean = RespBean.ok("登录成功", principal);
                        String s = new ObjectMapper().writeValueAsString(respBean);
                        resp.getWriter().write(s);
                    })
                    //登录失败的回调
                    .failureHandler((req, resp, e) -> {
                        resp.setContentType("application/json;charset=utf-8");
                        //登录失败可能会有多种原因
                        RespBean respBean = RespBean.error("登录失败");
                        if (e instanceof BadCredentialsException) {
                            respBean.setMsg("用户名或者密码输入错误,登录失败");
                        } else if (e instanceof UsernameNotFoundException) {
                            //默认情况下,这个分支是不会进来的,Spring Security 自动隐藏了了这个异常,如果系统中发生了 UsernameNotFoundException 会被自动转为 BadCredentialsException 异常然后抛出来
                        } else if (e instanceof LockedException) {
                            //如果 com.qfedu.security02.model.User.isAccountNonLocked 方法返回 false,就会进入到这里来
                            respBean.setMsg("账户被锁定,登录失败");
                        } else if (e instanceof AccountExpiredException) {
                            //com.qfedu.security02.model.User.isAccountNonExpired
                            respBean.setMsg("账户过期,登录失败");
                        } else if (e instanceof CredentialsExpiredException) {
                            respBean.setMsg("密码过期,登录失败");
                        } else if (e instanceof DisabledException) {
                            respBean.setMsg("账户被禁用,登录失败");
                        }
                        ObjectMapper om = new ObjectMapper();
                        String s = om.writeValueAsString(respBean);
                        PrintWriter out = resp.getWriter();
                        out.write(s);
                    })
                    .and()
                    //关闭 csrf 防御机制,这个 disable 方法本质上就是从 Spring Security 的过滤器链上移除掉 csrf 过滤器
                    .csrf().disable()
                    .exceptionHandling()
                    //如果用户未登录就访问某一个页面,就会触发当前方法
                    .authenticationEntryPoint((req, resp, authException) -> {
                        resp.setContentType("application/json;charset=utf-8");
                        RespBean respBean = RespBean.error("尚未登录,请登录");
                        String s = new ObjectMapper().writeValueAsString(respBean);
                        resp.getWriter().write(s);
                    });

            return http.build();
        }
    }

2.2 service

package com.huang.springsecurity.service;

import com.huang.springsecurity.mapper.UserMapper;
import com.huang.springsecurity.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserService implements UserDetailsService, UserDetailsPasswordService {

    @Autowired
    UserMapper userMapper;


    /**
     * 根据用户名查询用户对象
     * @param username 用户登录时候输入的用户名
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User u = userMapper.loadUserByUsername(username);
        if (u == null) {
            //说明用户名不存在
            throw new UsernameNotFoundException("账户不存在");
        }
        return u;
    }
}    

2.2 mapper

UserMapper

package com.huang.springsecurity.mapper;

import com.huang.springsecurity.model.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    User loadUserByUsername(String username);

    void updatePassword(User u);

}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.huang.springsecurity.mapper.UserMapper">
    <select id="loadUserByUsername" resultType="com.huang.springsecurity.model.User">
            select * from t_user where username=#{username};
    </select>

    <update id="updatePassword">
        update t_user set password=#{password} where id=#{id}
    </update>
</mapper>

三.测试详情

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值