• Spring Security构建Rest服务-0702-个性化用户认证流程2


    登录成功后的处理AuthenticationSuccessHandler:

    认证成功后,默认情况下spring security会继续访问之前访问的url,如果想自定义处理逻辑,用默认的就不行了。此时需要自定义登录成功后的处理,springsecurity提供了一个接口,AuthenticationSuccessHandler:

    有三个参数,request,response,authentication(封装认证的信息,认证请求里的信息ip、session等)

    public interface AuthenticationSuccessHandler {
    
        /**
         * Called when a user has been successfully authenticated.
         *
         * @param request the request which caused the successful authentication
         * @param response the response
         * @param authentication the <tt>Authentication</tt> object which was created during
         * the authentication process.
         */
        void onAuthenticationSuccess(HttpServletRequest request,
                HttpServletResponse response, Authentication authentication)
                throws IOException, ServletException;
    
    }

    这个接口只有一个方法,登录成功后就被调用。

    1、自定义一个handler,实现这个接口:

    package com.imooc.security.browser.authentication;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    
    /**
     * 认证成功后做的处理
     * ClassName: ImoocAuthenticationSuccessHandler 
     * @Description:  认证成功后做的处理
     * @author lihaoyang
     * @date 2018年3月1日
     */
    @Component("imoocAuthenticationSuccessHandler")
    public class ImoocAuthenticationSuccessHandler implements AuthenticationSuccessHandler{
    
        private Logger logger = LoggerFactory.getLogger(getClass());
        
        //springmvc启动会自动注册一个ObjectMapper
        @Autowired
        private ObjectMapper objectMapper;
        
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
    
            logger.info("登录成功");
            //把authentication返回给响应
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(authentication));
        }
    
    }

    2、添加到配置类,让spring security执行自定义的处理器

    package com.imooc.security.browser;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    
    import com.imooc.security.core.properties.SecurityProperties;
    
    @Configuration //这是一个配置
    public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter{
        
        //读取用户配置的登录页配置
        @Autowired
        private SecurityProperties securityProperties;
        
        //自定义的认证后的处理器
        @Autowired
        private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
        
    
        //注意是org.springframework.security.crypto.password.PasswordEncoder
        @Bean
        public PasswordEncoder passwordencoder(){
            //BCryptPasswordEncoder implements PasswordEncoder
            return new BCryptPasswordEncoder();
        }
        
        
        
        //版本一:配置死的登录页
    //    @Override
    //    protected void configure(HttpSecurity http) throws Exception {
    //        //实现需要认证的接口跳转表单登录,安全=认证+授权
    //        //http.httpBasic() //这个就是默认的弹框认证
    //        http.formLogin() //表单认证
    //            .loginPage("/login.html") //登录页面
    //            //登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
    //            .loginProcessingUrl("/authentication/form") 
    //            .and()
    //            .authorizeRequests() //下边的都是授权的配置
    //            .antMatchers("/login.html").permitAll() //放过登录页不过滤,否则报错
    //            .anyRequest()        //任何请求
    //            .authenticated()    //都需要身份认证
    //            .and()
    //            .csrf().disable() //关闭csrf防护
    //            ;    
    //    }
        
        //版本二:可配置的登录页
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            //实现需要认证的接口跳转表单登录,安全=认证+授权
            //http.httpBasic() //这个就是默认的弹框认证
            http.formLogin() //表单认证
                .loginPage("/authentication/require") //处理用户认证BrowserSecurityController
                //登录过滤器UsernamePasswordAuthenticationFilter默认登录的url是"/login",在这能改
                .loginProcessingUrl("/authentication/form") 
                .successHandler(imoocAuthenticationSuccessHandler)//自定义的认证后处理器
                .and()
                .authorizeRequests() //下边的都是授权的配置
                // /authentication/require:处理登录,securityProperties.getBrowser().getLoginPage():用户配置的登录页
                .antMatchers("/authentication/require",securityProperties.getBrowser().getLoginPage()).permitAll() //放过登录页不过滤,否则报错
                .anyRequest()        //任何请求
                .authenticated()    //都需要身份认证
                .and()
                .csrf().disable() //关闭csrf防护
                ;    
        }
    }

    启动springboot,访问localhost:8080/index.html ,跳转到登录页:

    登录,响应了自定义的处理器,返回了Authentication的json:其中粉色部分是自定义的登录处理MyUserDetailService类的loadUserByUsername方法返回值User类对应的json ,而且spring security还会自动把密码置为null

    {
        "authorities": [{
            "authority": "admin"
        }],
        "details": {
            "remoteAddress": "0:0:0:0:0:0:0:1",
            "sessionId": "DBDC15FA0FFEFD35B011431454B4F466"
        },
        "authenticated": true,
        "principal": {
            "password": null,
            "username": "user",
            "authorities": [{
                "authority": "admin"
            }],
            "accountNonExpired": true,
            "accountNonLocked": true,
            "credentialsNonExpired": true,
            "enabled": true
        },
        "credentials": null,
        "name": "user"
    }

    MyUserDetailService:

    package com.imooc.security.browser;
    
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.authority.AuthorityUtils;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    import org.springframework.security.core.userdetails.UsernameNotFoundException;
    import org.springframework.security.crypto.password.PasswordEncoder;
    import org.springframework.stereotype.Component;
    
    /**
     * UserDetailsService是SpringSecurity的一个接口,
     * 只有一个方法:根据用户名获取用户详情
     * ClassName: MyUserDetailService 
     * @Description: TODO
     * @author lihaoyang
     * @date 2018年2月28日
     */
    @Component
    public class MyUserDetailService implements UserDetailsService{
        
        private Logger logger = LoggerFactory.getLogger(getClass());
        
        @Autowired
        private PasswordEncoder passwordEncoder;
        
        /**
         * UserDetails接口,实际可以自己实现这个接口,返回自己的实现类
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            logger.info("登录用户名:"+username);
            //根据用户名查询用户信息
            
            //User:springsecurity 对 UserDetails的一个实现
            //为了演示在这里用passwordEncoder加密一下密码,实际中在注册时就加密,此处直接拿出密码
            String password = passwordEncoder.encode("123456");
            System.err.println("加密后密码:  "+password);
            //参数:用户名|密码|是否启用|账户是否过期|密码是否过期|账户是否锁定|权限集合
            return new User(username,password,true,true,true,true,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
        }
    
    }

    登录失败的处理AuthenticationFailureHandler:

    和登录成功的处理一样

    package com.imooc.security.browser.authentication;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    /**
     * 登录失败后的处理
     * ClassName: ImoocAuthenticationFailureHandler 
     * @Description: 登录失败后的处理
     * @author lihaoyang
     * @date 2018年3月1日
     */
    @Component("imoocAuthenticationFailureHandler")
    public class ImoocAuthenticationFailureHandler implements AuthenticationFailureHandler {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
        
        //springmvc启动会自动注册一个ObjectMapper
        @Autowired
        private ObjectMapper objectMapper;
        
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            
            logger.info("登录失败");
            
            //把authentication返回给响应
            //状态码500,服务器内部错误
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(objectMapper.writeValueAsString(exception));
        }
    
    }

    在java配置类里注入,然后配置一下就行了

     

     启动springboot,访问登录页http://localhost:8080/demo-login.html,填写错误的密码:

    响应500,坏的凭证。和一些堆栈信息

    到目前为止,自定义登录处理逻辑已经做完了,此时做的效果都是返回的json,如果系统里是登录成功后跳转页面的,这样还不够通用,需要做成返回json和重定向两种都适应的。

    要的效果是,能够在application.properties里做登录类型的配置。如:

    需要在配置类 SecurityProperties 里加上这个loginType,新建枚举类:

    package com.imooc.security.core.properties;
    
    /**
     * 登录类型枚举
     * ClassName: LoginType 
     * @Description: 登录类型
     * @author lihaoyang
     * @date 2018年3月1日
     */
    public enum LoginType {
    
        /**
         * 返回json
         */
        JSON,
        
        /**
         * 重定向
         */
        REDIRECT
    }

    BrowserProperties :

    package com.imooc.security.core.properties;
    
    /**
     * 浏览器配置项
     * ClassName: BrowserProperties 
     * @Description: 浏览器配置项
     * @author lihaoyang
     * @date 2018年2月28日
     */
    public class BrowserProperties {
    
        //用户未配置默认登录页
        private String loginPage = "/login.html"; 
    
        private LoginType loginType = LoginType.JSON ;
        
        public String getLoginPage() {
            return loginPage;
        }
    
        public void setLoginPage(String loginPage) {
            this.loginPage = loginPage;
        }
    
        public LoginType getLoginType() {
            return loginType;
        }
    
        public void setLoginType(LoginType loginType) {
            this.loginType = loginType;
        }
        
    }

    动态配置登录类型

    改造登录处理器:

    登录成功处理器改造为:

    继承一个spring默认的实现类 extends SavedRequestAwareAuthenticationSuccessHandler ,引入配置类,就能读取properties配置文件,进行判断了:
    package com.imooc.security.browser.authentication;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.imooc.security.core.properties.BrowserProperties;
    import com.imooc.security.core.properties.LoginType;
    import com.imooc.security.core.properties.SecurityProperties;
    
    
    /**
     * 认证成功后做的处理
     * ClassName: ImoocAuthenticationSuccessHandler 
     * @Description:  认证成功后做的处理
     * @author lihaoyang
     * @date 2018年3月1日
     */
    @Component("imoocAuthenticationSuccessHandler")
    public class ImoocAuthenticationSuccessHandler 
            //spring默认的登录成功处理器,实现了AuthenticationSuccessHandler接口,适配登录后 重定向和返回json两种用这个实现
            extends SavedRequestAwareAuthenticationSuccessHandler 
            //返回json用这个实现
            /*implements AuthenticationSuccessHandler*/{
    
        private Logger logger = LoggerFactory.getLogger(getClass());
        
        //springmvc启动会自动注册一个ObjectMapper
        @Autowired
        private ObjectMapper objectMapper;
        
        @Autowired
        private SecurityProperties securityProperties;
        
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
                Authentication authentication) throws IOException, ServletException {
    
            logger.info("登录成功");
            
            /**
             * 判断配置的登录类型,做不同处理
             */
            if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
                //响应json
                //把authentication返回给响应
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(objectMapper.writeValueAsString(authentication));
            }else{
                //调用父类的方法,默认就是重定向
                super.onAuthenticationSuccess(request, response, authentication);
            }
            
        }
    
    }

     登录失败的处理改造为:

    package com.imooc.security.browser.authentication;
    
    import java.io.IOException;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpStatus;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.AuthenticationFailureHandler;
    import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
    import org.springframework.stereotype.Component;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.imooc.security.core.properties.BrowserProperties;
    import com.imooc.security.core.properties.LoginType;
    import com.imooc.security.core.properties.SecurityProperties;
    
    /**
     * 登录失败后的处理
     * ClassName: ImoocAuthenticationFailureHandler 
     * @Description: 登录失败后的处理
     * @author lihaoyang
     * @date 2018年3月1日
     */
    @Component("imoocAuthenticationFailureHandler")
    public class ImoocAuthenticationFailureHandler 
            extends SimpleUrlAuthenticationFailureHandler
            /*implements AuthenticationFailureHandler*/ {
    
        private Logger logger = LoggerFactory.getLogger(getClass());
        
        //springmvc启动会自动注册一个ObjectMapper
        @Autowired
        private ObjectMapper objectMapper;
        
    
        @Autowired
        private SecurityProperties securityProperties;
        
        @Override
        public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
                AuthenticationException exception) throws IOException, ServletException {
            
            logger.info("登录失败");
            if(LoginType.JSON.equals(securityProperties.getBrowser().getLoginType())){
                //把authentication返回给响应
                //状态码500,服务器内部错误
                response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
                response.setContentType("application/json;charset=UTF-8");
                response.getWriter().write(objectMapper.writeValueAsString(exception));
            }else{
                super.onAuthenticationFailure(request, response, exception);
            }
        }
    }

    这样,在application.properties 里配置 imooc.security.browser.loginType = REDIRECT 或 imooc.security.browser.loginType = JSON 就可以动态控制登录成功后改返回json还是重定向了。

    在demo项目新建一个index.html:

    <body>
    <h2>Index Page</h2> <br>
    </body>

    配置登录类型为REDIRECT,访问 localhost:8080/index.html  让登录

    登录成功后,重定向到了index.html

    设置为JSON登录:

    #登录类型
    imooc.security.browser.loginType = JSON

    再访问localhost:8080/index.html :

    响应我们自己处理的登录逻辑。

    完整代码放在了github :https://github.com/lhy1234/spring-security

  • 相关阅读:
    要对一个任意进程(包括系统安全进程和服务进程)进行指定了相关的访问权 [OpenProcessToken,LookupPrivilegeValue,AdjustTokenPrivileges]
    XPath 表达式的上下文
    Visual C++ ADO数据库编程入门
    sscanf 详细说明
    asp.net 文件下载
    [转]JavaScript 获得页面区域大小的代码
    IE与Firefox等浏览器对容器width的不同解释及解决办法
    编译Boost_1_37_0 For VS2008
    asp.net cookies
    [转]C++字符串格式化 sprintf
  • 原文地址:https://www.cnblogs.com/lihaoyang/p/8486842.html
Copyright © 2020-2023  润新知