欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-19 15:02:04
...

首先我们要明白,自动登录就是把我们的登录信息保存到客户端也就是浏览器的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) {
                    }
                });
    }
}

相关标签: 安全框架