Spring Security 自定义注销

Sho*_*lah 3 spring-mvc spring-security spring-boot

我有一个使用 Spring 安全性的 SpringBoot 应用程序,但我想在身份验证而不是授权上对其进行自定义。我已成功登录,但我不知道我必须将注销操作放在哪里。以下是我的一些代码: 1. 控制器:

    @RequestMapping(value={"/login"}, method=RequestMethod.GET)
    public ModelAndView login(){

    return new ModelAndView("pages/login");

    }
Run Code Online (Sandbox Code Playgroud)
  1. 网络安全配置

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
        private @Autowired CustomAuthenticationProvider authenticationManager;
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
            .authorizeRequests()
                .anyRequest()
                    .authenticated()
                .and()
                .formLogin()
                    .usernameParameter("username")
                    .passwordParameter("password")
                    .failureUrl("/login?error")
                    .defaultSuccessUrl("/")
                    .loginPage("/login")
                    .permitAll()
                .and()
                    .logout()
                    .logoutRequestMatcher(
                            new AntPathRequestMatcher("/login?logout")
                    ).logoutSuccessUrl("/login").permitAll()
                .and()
                .csrf().disable();
        }
    
        @Autowired
        @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
          auth.authenticationProvider( authenticationManager );
        }
    
        @Bean
        protected AuthenticationProvider getServicesAuthenticationProvider() {
        //stackoverflow.com/questions/22453550/custom-authentication-provider-not-being-called/22457561#22457561
             return new CustomAuthenticationProvider();
        }
    
        @Override
        public void configure(WebSecurity web) throws Exception {
            web.ignoring()
                    .antMatchers("/**/*.css")
                    .antMatchers("/**/*.js")
                    .antMatchers("/**/*.jpg")
                    .antMatchers("/**/*.png")
                    .antMatchers("/**/*.gif")
                    .antMatchers("/resources/**");
        }
        public PasswordEncoder passwordEncoder(){
            PasswordEncoder encoder = NoOpPasswordEncoder.getInstance();
            return encoder;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 自定义身份验证提供程序

    @Component
    public class CustomAuthenticationProvider implements AuthenticationProvider{
        private static Logger logger = Logger.getLogger(CustomAuthenticationProvider.class);
        private @Autowired UAMSLogin uamsLogin;
        private Map<String, Boolean> userLoggedIn = new HashMap<String, Boolean>();
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            String username = authentication.getPrincipal() + "";
            String password = authentication.getCredentials() + "";
            logger.info("authenticating....");
            if(username.equals("")|| username==null || password.equals("")|| password==null){
                logger.fatal("username or password cannot be empty!");
                return null;
            }
            else if(userLoggedIn.containsKey(username)){
                UsernamePasswordAuthenticationToken a = new UsernamePasswordAuthenticationToken(username, password);
                return a;
            }
            try {
                if (uamsLogin.loginUams(username, password)) {
                    logger.info("authentication success");
                    UamsSession sessionInfo = uamsLogin.getUams();
                    logger.info("authentication success");
                    String role = "USER";
                    userLoggedIn.put(username, true);
                    UsernamePasswordAuthenticationToken a = new UsernamePasswordAuthenticationToken(username, password);
                    return a;
                }
            } catch (Exception e) {
                // TODO Auto-generated catch block
                logger.info("authentication failed");
                e.printStackTrace();
                throw new BadCredentialsException("1000");
            }
            return null;
        }
    
        @Override
        public boolean supports(Class<?> authentication) {
            return authentication.equals(UsernamePasswordAuthenticationToken.class);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. UAMS登录:

    @Component
    public class UAMSLogin implements Serializable {
    
    
        private static Logger logger = Logger.getLogger(UAMSLogin.class);
        private static final long serialVersionUID = 1L;
        private static boolean isConnected = false;
        private UamsSession session;
        @Value("${UAMS.SEC_SRV_CONN}")
        private String UAMS_SERVER_CONNECTION;
    
        @Value("${UAMS.CSM_SERVER_URL}")
        private String UAMS_CSM_SERVER_URL;
    
        @Value("${amdocs.ticketapplicationid}")
        private String amdocs_ticketapplicationid;
    
        @Value("${amdocs.ticketparam}")
        private String amdocs_ticketparam;  
        public UAMSLogin(){
    
        }
        // Login with UAMS
        public boolean loginUams(String username, String password) throws Exception {
            logger.info("loginUams with " + username + "/" + password);
            session = this.createSession();
            logger.info("create session success: "+session.toString());
            String ticket=null;
    
            logger.info("UamsSystem version: "+UamsSystem.getVersionString());
            try {
                session.ensureSession(username, password);
                ticket = session.getTicket();
                if (ticket != null && ticket !="") {
                    logger.info("login success : " + session.getTicket());
                    isConnected = true;
                    return true;
                } else {
                    logger.info("login failed: ticket is NULL");
                    return false;
                }
            } catch (Exception e) {
                logger.info("login failed: ", e);
                return false;
            }
        }
    
        protected UamsSession createSession() throws Exception {
            UamsSession session     = new UamsSession(ReadConfig.readInputStream());
    
            logger.info("UAMS_SERVER_CONNECTION: "+UAMS_SERVER_CONNECTION);
            logger.info("UAMS_CSM_SERVER_URL: "+UAMS_CSM_SERVER_URL);
            logger.info("amdocs_ticketapplicationid: "+amdocs_ticketapplicationid);
            logger.info("amdocs_ticketparam:  "+amdocs_ticketparam);
            session.setSecurityUrl(UAMS_SERVER_CONNECTION);
            session.setProviderUrl(UAMS_CSM_SERVER_URL);
    
            session.setApplicationId(amdocs_ticketapplicationid);
            session.setParam(amdocs_ticketparam);
            return session;
        }
    
        public static boolean isConnected() {
            return isConnected;
        }
    
    
        public UamsSession getUams(){
            return session;
        }
    
    }
    
    Run Code Online (Sandbox Code Playgroud)

如您所见,我正在使用 UAMS 来查看此用户是否有效。我仍然不知道如何记住每个会话,因此您可以看到有脏代码 HashMap containsKey。更重要的是,如果我制作session.logout(username);我应该在哪里打电话??UAMS 是我公司使用的 Jar。

如果你发现这个问题不清楚,我很抱歉,请问我。任何帮助将不胜感激。

jlu*_*etu 5

如果我理解,我猜您想在 Spring Security 中注销后对您谈到的 UAMS 库执行一些操作。

如果这就是您所说的,我最好使用自定义LogoutSuccessHandler并将其在您的配置中绑定到注销操作。

LogoutSuccessHandler 接口只有一个您应该实现的方法:

public class CustomLogoutHandler implements LogoutSuccessHandler {

    private Logger logger = Logger.getLogger(this.getClass());

    /* (non-Javadoc)
     * @see org.springframework.security.web.authentication.logout.LogoutSuccessHandler#onLogoutSuccess(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request,
            HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException {

        logger.debug("CustomLogoutHandler.onLogoutSuccess");

    }

}
Run Code Online (Sandbox Code Playgroud)

在这里你可以做任何你需要的事情,然后甚至重定向到你在会话注销后应该去的任何页面。

编辑:

你应该像我说的那样使用 LogoutSuccessHandler 或者更好地添加一个LogoutHandler

public class TaskImplementingLogoutHandler implements LogoutHandler {

    /* (non-Javadoc)
     * @see org.springframework.security.web.authentication.logout.LogoutHandler#logout(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, org.springframework.security.core.Authentication)
     */
    @Override
    public void logout(HttpServletRequest req, HttpServletResponse res,
            Authentication authentication) {
        // do whatever you need
    }

} 
Run Code Online (Sandbox Code Playgroud)

一旦您实现了适合您需要的 LogoutHandler,您应该将其添加到您的配置中。看看java安全配置注销部分

你的java安全配置必须是这样的:

@Override
    protected void configure(HttpSecurity http) throws Exception {
        http
        .authorizeRequests()
            .anyRequest()
                .authenticated()
            .and()
            .formLogin()
                .usernameParameter("username")
                .passwordParameter("password")
                .failureUrl("/login?error")
                .defaultSuccessUrl("/")
                .loginPage("/login")
                .permitAll()
            .and()
                .logout()
                .logoutRequestMatcher(
                        new AntPathRequestMatcher("/login?logout")
                )
                .addLogoutHandler(new TaskImplementingLogoutHandler())
                .logoutSuccessUrl("/login").permitAll()
            .and()
            .csrf().disable();
    }
Run Code Online (Sandbox Code Playgroud)