Spring Security(学习笔记) --Json登录的两种方式!

重点标识

Json登录。
Security默认是key-value的形式的。

前后端分离,Json处理比较方便。

Security默认是通过request.getpartmater这种方式获取,所以不支持。

自定义登录接口(方法一)

上一篇已经说过了,Spring容器里面是没有AuhtenticationManager,但是有AuhtenticationManagerBuilder。

创建一个登录接口:


@Controller
public class LoginController {

    @Autowired
    AuthenticationManager authenticationManager;

    @PostMapping("/login")
    @ResponseBody
    public String login(@RequestBody User user, HttpSession session){

        //未认证令牌,参考UsernamePasswordAuthenticationFilter这个来写就行了
        try {
            UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(user.getUsername(), user.getPassword());

            Authentication auth = authenticationManager.authenticate(authRequest);

            //这里还有一点要注意,我们一般都是从SecurityContextHolder取到用户信息的,那这里也别忘了给他放进去
            SecurityContextHolder.getContext().setAuthentication(auth);
            //还有一点要注意,新版的Security是从session中获取的,那本次会话的下次请求想要拿到,也需要丢到session中去
            session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY,SecurityContextHolder.getContext());
            return auth.getName();

        } catch (AuthenticationException e) {
            //throw new RuntimeException(e);
            //如果出现错误,这里简单点,就不封装了,直接返回好了
            return e.getMessage();
        }
    }
}

向Spring容器中注入一个AuthenticationManager 。


@Configuration
public class SecurityConfig {


    UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("admin").password("{noop}123").build());
        return inMemoryUserDetailsManager;
    }

    @Bean
    AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
       daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests(a->a.requestMatchers("/login").permitAll().anyRequest().authenticated())
                .csrf(c->c.disable());
        return http.build();
    }

}

User类也给大家


public class User {

    private String username;

    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

看一下测试结果:

在这里插入图片描述
再看一下,同一个会话中,另一个请求能不能获取到用户信息:

在这里插入图片描述
ok ,都没问题,实际上,像我们之前写的验证码,如果不想采用过滤器的形式,也可以通过自定义登录这种方式,写在authenticate之前,try里面,这样验证错误被捕捉,也可以直接返回。

修改过滤器UsernamePasswordAuthenticationFilter(方法二)

准备一个过滤器,继承自UsernamePasswordAuthenticationFilter,在它的基础上,改一改就行了。


public class JsonFilter extends UsernamePasswordAuthenticationFilter {

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

            String contentType = request.getContentType();
            if (contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE) || contentType.equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
                //json请求
                String username = null;
                String password = null;
                try {
                    User user = new ObjectMapper().readValue(request.getInputStream(), User.class);
                    username = user.getUsername();
                    username = username != null ? username.trim() : "";
                    password = user.getPassword();
                    password = password != null ? password : "";
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }


                UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username, password);
                this.setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            } else {
                //key -value
                return super.attemptAuthentication(request, response);

            }
        }
    }
}

然后,配置一下。


@Configuration
public class SecurityConfig {


    @Bean
    JsonFilter jsonFilter(){
        JsonFilter jsonFilter = new JsonFilter();
        jsonFilter.setFilterProcessesUrl("/login");
        jsonFilter.setAuthenticationSuccessHandler(((request, response, authentication) -> {

            response.getWriter().write(authentication.getName());
        }));

        jsonFilter.setAuthenticationFailureHandler(((request, response, exception) -> {
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(exception.getMessage());
        }));


        jsonFilter.setAuthenticationManager(authenticationManager());
        //配置Security的存储策略,如果我们没有定义jsonFilter,系统会给UsernamePasswordAuthenticationFilter设置SecurityContextRepository
        //这个说白了,就是,同一个会话的后续请求认证也要存上
        jsonFilter.setSecurityContextRepository(new HttpSessionSecurityContextRepository());
        return jsonFilter;
    }

    UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        inMemoryUserDetailsManager.createUser(User.withUsername("admin").password("{noop}123").build());
        return inMemoryUserDetailsManager;
    }

    @Bean
    AuthenticationManager authenticationManager(){
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
       daoAuthenticationProvider.setUserDetailsService(userDetailsService());
        ProviderManager providerManager = new ProviderManager(daoAuthenticationProvider);
        return providerManager;
    }

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests(a->a.anyRequest().authenticated())
                //.addFilter()  添加一个过滤器,但是自动排序,一般不用这个
                //添加一个过滤器,在某一个过滤器前面
                .addFilterBefore(jsonFilter(), UsernamePasswordAuthenticationFilter.class)
                .csrf(c->c.disable());
        return http.build();
    }

}

这样就ok了,同理,之前的验证码,也可以在这个过滤器里面实现的。

结语

生命不息,奋斗不止,加油!

相关推荐

  1. FATFS学习笔记——FATFS写文件方式

    2024-04-29 14:42:07       33 阅读
  2. 使用JWT、Redis + token实现用户登录方式

    2024-04-29 14:42:07       14 阅读
  3. Golang实践录:gin绑定解析json方法

    2024-04-29 14:42:07       32 阅读
  4. 绕过Microsoft登录:安装Windows 11 23H2方法

    2024-04-29 14:42:07       16 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-04-29 14:42:07       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-04-29 14:42:07       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-04-29 14:42:07       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-04-29 14:42:07       18 阅读

热门阅读

  1. Vue.js(过渡)

    2024-04-29 14:42:07       12 阅读
  2. Linux内核驱动开发-001字符设备开发-002led杂项驱动

    2024-04-29 14:42:07       10 阅读
  3. Stylus入门使用方法

    2024-04-29 14:42:07       12 阅读
  4. UKP3D轴侧图出图按照哪些标准

    2024-04-29 14:42:07       9 阅读
  5. 在docker中安装paddle serving @FreeBSD(待续)

    2024-04-29 14:42:07       10 阅读
  6. c++课堂——动态规划

    2024-04-29 14:42:07       13 阅读
  7. 【排序算法】快速排序

    2024-04-29 14:42:07       11 阅读