Spring Security 多登录实现

需要先增加一个自定义的Filter去继承 UsernamePasswordAuthenticationFilter 或者 AbstractAuthenticationProcessingFilter

然后在自定义的Filter里面指定登录的Url . 设置过滤器的时候,必须为过滤器指定一个 authenticationManager ,并且初始化构造函数的时候,要传入该manager.

再编写一个Provider , 把自定义的UserDetailService传给该provider.

具体实现过程如下:

添加配置:

   @Override
    protected void configure(AuthenticationManagerBuilder auth) {
        //增加自定义的UserDetailService
        userDetailsAuthenticationProvider.setUserDetailsService(userDetailsService);
        //设置一个Provider
        auth.authenticationProvider(userDetailsAuthenticationProvider);

    }

关键配置:

   @Override
    protected void configure(HttpSecurity http) throws Exception {
        //手动实现的权限验证管理器
        movieAuthorizeConfigProviderManager.configure(http.authorizeRequests());

        //添加前台登录验证过滤器与userDetailService,不同的登录处理URL需要在Filter里面指定
        UserAuthenticationFilter userAuthenticationFilter = new UserAuthenticationFilter();
        //每个Filter必须指定一个authenticationManager
        userAuthenticationFilter.setAuthenticationManager(authenticationManager());
        //设置登录成功处理事件
        userAuthenticationFilter.setAuthenticationSuccessHandler(movieAuthenticationSuccessHandler);
        //设置登录失败处理事件
        userAuthenticationFilter.setAuthenticationFailureHandler(movieAuthenticationFailureHandler);
        http.addFilterBefore(userAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);  }

完整配置:

自定义过滤器:

public class MyAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    public static final String POST = "POST";

    public MyAuthenticationFilter() {
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/user/login/check", "POST"));
        this.setAuthenticationManager(getAuthenticationManager());
    }

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

        String username = obtainUsername(request);
        String password = obtainPassword(request);

        if (username == null) {
            username = "";
        }

        if (password == null) {
            password = "";
        }

        username = username.trim();

        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
                username, password);
        // Allow subclasses to set the "details" property
        setDetails(request, authRequest);
        return this.getAuthenticationManager().authenticate(authRequest);
    }
}

Provider:

@Component
public class UserDetailsAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    private volatile String userNotFoundEncodedPassword;
    private static final String USER_NOT_FOUND_PASSWORD = "userNotFoundPassword";

    @Autowired
    private PasswordEncoder passwordEncoder;
    private UserDetailsService userDetailsService;

    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            logger.debug("Authentication failed: password does not match stored value");
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }

    @Override
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        prepareTimingAttackProtection();
        try {
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
            if (loadedUser == null) {
                throw new InternalAuthenticationServiceException(
                        "UserDetailsService returned null, which is an interface contract violation");
            }
            return loadedUser;
        } catch (UsernameNotFoundException ex) {
            mitigateAgainstTimingAttack(authentication);
            throw ex;
        } catch (InternalAuthenticationServiceException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
        }
    }

    private void prepareTimingAttackProtection() {
        if (this.userNotFoundEncodedPassword == null) {
            this.userNotFoundEncodedPassword = this.passwordEncoder.encode(USER_NOT_FOUND_PASSWORD);
        }
    }

    private void mitigateAgainstTimingAttack(UsernamePasswordAuthenticationToken authentication) {
        if (authentication.getCredentials() != null) {
            String presentedPassword = authentication.getCredentials().toString();
            this.passwordEncoder.matches(presentedPassword, this.userNotFoundEncodedPassword);
        }
    }

    public UserDetailsService getUserDetailsService() {
        return userDetailsService;
    }

    public void setUserDetailsService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

}

UserDetailService:

@Component
@Slf4j
@Qualifier("normalUserDetailService")
public class UserDetailServiceImpl implements UserDetailsService {
    private final IUserDao userDao;

    public UserDetailServiceImpl(IUserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        club.cearnach.movie.entity.User user = userDao.findByAccount(username)
                .orElseThrow(() -> new UsernameNotFoundException("找不到指定的用户"));
        List<GrantedAuthority> authorities = AuthorityUtils
                .commaSeparatedStringToAuthorityList(
                        MovieSecurityConstants.ROLE_PREFIX.concat(user.getRole().getName()));
        return new User(user.getAccount(), user.getPassword(), authorities);
    }
}

第二个UserDetailService的实现:

@Component
@Qualifier("adminDetailService")
public class AdminUserDetailServiceImpl implements UserDetailsService {
    private final IAdminService adminService;

    public AdminUserDetailServiceImpl(IAdminService adminService) {
        this.adminService = adminService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Admin admin = adminService.findByAccount(username)
                .orElseThrow(() -> new UsernameNotFoundException(AdminException.ADMIN_CAN_NOT_FOUNT));
        List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList(
                MovieSecurityConstants.ROLE_PREFIX.concat(admin.getRole().getName()));
        return new User(admin.getAccount(), admin.getPassword(), authorities);
    }
}

原文地址:https://www.cnblogs.com/cearnach/p/9094227.html

时间: 2024-08-30 16:21:17

Spring Security 多登录实现的相关文章

Spring Security 自定义登录认证(二)

一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Security中有默认的密码加密方式以及登录用户认证校验,但小编这里选择自定义是为了方便以后业务扩展,比如系统默认带一个超级管理员,当认证时识别到是超级管理员账号登录访问时给它赋予最高权限,可以访问系统所有api接口,或在登录认证成功后存入token以便用户访问系统其它接口时通过token认证用户权限

springboot中使用spring security,登录url就出现403错误

参考链接:https://segmentfault.com/q/1010000012743613 有两个controller,一个是所有用户可以访问的@RequestMapping("user"),还有一个是管理员可以访问的@RequestMapping("admin"). /user/login是UserController中的登录url.所有操作(除登录注销)都要登录之后才能进行. 现在想用springboot结合spring security实现权限管理.系统

Spring Security 自定义登录验证与自定义回调地址

1 配置文件 security-ns.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:security="http://www

页面获取Spring Security登录用户

页面获取Spring Security登录用户 1.在session中取得spring security的登录用户名如下:${session.SPRING_SECURITY_CONTEXT.authentication.principal.username} spring security 把SPRING_SECURITY_CONTEXT 放入了session 没有直接把username 放进去.下面一段代码主要描述的是session中的存的变量, 存跳转时候的URLsession {SPRIN

SpringBoot集成Spring Security(4)——自定义表单登录

通过前面三篇文章,你应该大致了解了 Spring Security 的流程.你应该发现了,真正的 login 请求是由 Spring Security 帮我们处理的,那么我们如何实现自定义表单登录呢,比如添加一个验证码- 源码地址:https://github.com/jitwxs/blog_sample 文章目录 一.添加验证码 1.1 验证码 Servlet 1.2 修改 login.html 1.3 添加匿名访问 Url二.AJAX 验证三.过滤器验证 3.1 编写验证码过滤器 3.2 注

初始spring security(一)

第一次接触spring security,第一个例子是最简单,实现的功能也仅仅是权限控制一些最基本的功能: 首先是web.xml文件: 1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="2.5" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www

Spring Security Session Time Out

最近在用Spring Security做登录管理,登陆成功后,页面长时间无操作,超过session的有效期后,再次点击页面操作,页面无反应,需重新登录后才可正常使用系统. 为了优化用户体验,使得在session失效后,用户点击页面对服务器发起请求时,页面能够自动跳转到登录页面.本次使用spring security 3.1. 第一步:配置spring security的专用配置文件spring-security.xml. <http auto-config="true" entr

Spring Security应用开发(09)密码错误次数限制

实现登录时密码错误次数限制功能,就是在登录界面中当用户提交了错误的密码时在数据库中记录下这个错误次数,直到错误次数达到指定次数时,锁定用户账户,此时即便输入正确的密码,也不能登录. 需要完成如下工作: (1)修改用户表users的结构,增加相关字段. (2)自定义实现UserDetailsService,用于加载额外的数据字段. (3)自定义实现AuthenticationProvider,用于捕获登录成功和失败的事件. (3)修改spring-security.xml文件,配置上述(2)和(3

Spring Security+Spring MVC+Mybatis

项目环境:JDK8+maven3.0+MySQL 项目结构: pom.xml: <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=&qu