【阿里小哥告诉我】SpringSecurity与自动登录,注销登录的爱恨情仇

首先我们要明白,自动登录就是把我们的登录信息保存到客户端也就是浏览器的cookie中,当我们下次再访问某个网站时,自动实现登录。从而会想到另外一个层面我们在开发系统是提升了用户登录的体验,但是会潜在的安全隐患。这就为我们主人公SpringSecurity主人公上场做好了铺垫,SpringSecurity提供了两种非常好的令牌:
 
官方:
 
1、用散列算法加密用户的登录信息并生成令牌。
2、持久性数据存储机制用的持久化令牌–比如数据库持久性技术

package com.zcw.demospringsecurity.demo8;

import com.zcw.demospringsecurity.demo4.User;
import com.zcw.demospringsecurity.demo4.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

/**
 * @ClassName : MyUserDetailsService
 * @Description :
 * @Author : Zhaocunwei
 * @Date: 2020-04-11 16:49
 */
@Service
public class MyUserDetailsService implements UserDetailsService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User byUserName = userMapper.findByUserName(username);
        if(byUserName ==null){
            throw new UsernameNotFoundException("数据不存在");
        }
        byUserName.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(byUserName.getRoles()));
        return byUserName;
    }
}

package com.zcw.demospringsecurity.demo8;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @ClassName : WebSecurityConfig
 * @Description : 方式1:SpringSecurity-散列加密方案实现自动登录
 * @Author : Zhaocunwei
 * @Date: 2020-04-11 16:55
 */
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService userDetailsService;


    @Override
    protected void configure(HttpSecurity http) throws Exception{
        http.authorizeRequests()
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/user/**").hasRole("USER")
                .antMatchers("/api/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .csrf()
                .disable()
                .formLogin()
                .and()
                //增加自动登录功能,默认为简单散列加密  --通过查看源码,默认过期时间为两个星期
                .rememberMe().userDetailsService(userDetailsService)
                .key("zcw");

        /**
         * 为什么自定义key,是因为通过查看源代码,
         * 在没有指定可以的情况下,系统会默认使用一个UUID字符串
         * 同时SpringSecurity在每次表单登录成功之后会更新此令牌,
         * 结合我们当下微服务架构盛行,分布式部署架构,如果每次都更新key,
         * 就会出现数据不同步问题,造成用户实现自动登录cookie失效,所以在
         * 实际项目开发中,我们要合理的创建key
         */
    }
}

package com.zcw.demospringsecurity.demo8;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.sql.DataSource;

/**
 * @ClassName : MyUserDetailsService2
 * @Description :方式2:SpringSecurity-持久性数据存储机制用的持久化令牌
 * @Author : Zhaocunwei
 * @Date: 2020-04-11 20:48
 */
@EnableWebSecurity
public class WebSecurityConfig2 extends WebSecurityConfigurerAdapter {
    @Autowired
    private MyUserDetailsService userDetailsService;
    @Autowired
    private DataSource dataSourece; //JdbcTokenRepositoryImpl是基于此类实现对应SQL操作的类

    @Override
    protected void configure(HttpSecurity http) throws Exception{
        JdbcTokenRepositoryImpl jdbcTokenRepository  = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSourece);
        http.authorizeRequests()
                .antMatchers("/admin/**")
                .hasAuthority("ROLE_ADMIN")
                .antMatchers("/user/**")
                .hasRole("USER")
                .antMatchers("/app/**")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .csrf()
                .disable()
                .formLogin()
                .and()
                .rememberMe()
                .userDetailsService(userDetailsService)
                .tokenRepository(jdbcTokenRepository);
    }
}
package com.zcw.demospringsecurity.demo8;

import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @ClassName : WebSecurityConfigLogout
 * @Description : SpringSecurity实现注销登录
 * @Author : Zhaocunwei
 * @Date: 2020-04-11 23:08
 */
@EnableWebSecurity
public class WebSecurityConfigLogout extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 通过查看源码,我们大家会发现在Logout的清理过程是由多个LogoutHandler流式处理的
         */
        http.logout()
                //指定接收注销请求的路由
                .logoutUrl("/logOut")
                //注销成功,重定向到该路径下
                .logoutSuccessUrl("/")
                //注销成功的处理方式,不同于logoutSuccessUrl的重定向,logoutSuccessHandler
                //更加灵活,
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest httpServletRequest,
                                                HttpServletResponse httpServletResponse,
                                                Authentication authentication)
                            throws IOException, ServletException {

                    }
                })
                //使该用户的HttpSession失效
                .invalidateHttpSession(true)
                //注销成功,删除指定的cookie
                .deleteCookies("cookie1", "cookie2")
                //用于注销的处理句柄,允许自定义一些清理策略
                //事实上LogoutSuccessHandler也能做到
                .addLogoutHandler(new LogoutHandler() {
                    @Override
                    public void logout(HttpServletRequest httpServletRequest,
                                       HttpServletResponse httpServletResponse, Authentication authentication) {
                    }
                });
    }
}

发布了458 篇原创文章 · 获赞 15 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_32370913/article/details/105552343