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

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

程序员文章站 2022-06-06 22:06:09
...
Spring Security 自定义登录验证与自定义回调地址

博客分类: spring
securityspring
Java代码 
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.springframework.org/schema/security" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd"> 
 
    //需要过滤不被拦截的请求 
    <security:http pattern="/openapi/**" security="none" /> 
    <security:http pattern="/useraccounts/userprofile.json" security="none" /> 
    <security:http pattern="/useraccounts/register**" security="none" /> 
    
     //entry-point-ref 配置自定义登录 
    <security:http auto-config="false" entry-point-ref="authenticationEntryPoint"> 
        <security:intercept-url pattern="/backManage/**" access="ROLE_BACK_USER" /> 
        <security:intercept-url pattern="/mall/**"       access="ROLE_BACK_USER" /> 
        <security:intercept-url pattern="/thirdUser/**"  access="ROLE_USER" /> 
        <security:intercept-url pattern="/useraccounts/**" access="ROLE_USER" /> 
        <security:intercept-url pattern="/cart/**.html" access="ROLE_USER" /> 
        <security:intercept-url pattern="/ticket/**" access="ROLE_USER,ROLE_BACK_USER" /> 
        <security:intercept-url pattern="/order/**" access="ROLE_USER" /> 
        <security:intercept-url pattern="/comment/**" access="ROLE_USER" /> 
        <security:intercept-url pattern="/personal/**" access="ROLE_USER" /> 
        <security:intercept-url pattern="/favorite/**" access="ROLE_USER" /> 
     
        //需要替换的Filter顺序,配置自定义custom-filter时必须蔣auto-config="false",不然会报已经存在同样的过滤器错误 
        <security:custom-filter ref="myLoginFilter"  position="FORM_LOGIN_FILTER" /> 
        //登出配置 
        <security:logout logout-success-url="${local.service.url}"/> 
    </security:http> 
 
     //密码加密工具类 
    <bean id="encoder" class="org.springframework.security.authentication.encoding.ShaPasswordEncoder"/> 
    //认证管理器 
    <security:authentication-manager alias="authenticationManager"> 
        //UserDetailsService实现 主要用于用户的查询 
        <security:authentication-provider user-service-ref="userLoginService"> 
            <security:password-encoder  ref="encoder"> 
            </security:password-encoder> 
        </security:authentication-provider> 
    </security:authentication-manager> 
 
    <bean id="myLoginFilter" class="com.sale114.www.sercurity.MyUsernamePasswordAuthenticationFilter"> 
        <property name="authenticationManager" ref="authenticationManager"/> 
        <property name="authenticationFailureHandler" ref="failureHandler"/> 
        <property name="authenticationSuccessHandler" ref="successHandler"/> 
    </bean> 
 
    //成功登录后 
    <bean id="successHandler" class="com.sale114.www.sercurity.MySavedRequestAwareAuthenticationSuccessHandler"> 
        <property name="defaultTargetUrl" value="${local.service.url}"/> 
    </bean> 
    //登录失败 
    <bean id="failureHandler" class="com.sale114.www.sercurity.MySimpleUrlAuthenticationFailureHandler"> 
        <property name="defaultFailureUrl" value="${local.service.url}/login.html?validated=false"/> 
    </bean> 
     
    <bean id="authenticationEntryPoint" 
        class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint"> 
        <property name="loginFormUrl" value="${local.service.url}/login.html" /> 
    </bean> 
</beans> 
 
 
2 UserLoginServiceImpl 查询用户实现类 
 
     @Named("userLoginService") 
public class UserLoginServiceImpl  implements UserDetailsService ,LoginService{ 
 
    @Inject 
    private UserLoginDAO userLoginDAO; 
     
    @Override 
    public WrappedUserLogin getUserLogin() { 
        try { 
            WrappedUserLogin wrappedUserLogin = (WrappedUserLogin) SecurityContextHolder 
                    .getContext().getAuthentication().getPrincipal(); 
            return wrappedUserLogin; 
        } catch (Exception e) { 
            return null; 
        } 
    } 
 
    @Override 
    public UserDetails loadUserByUsername(String username) 
            throws UsernameNotFoundException { 
        System.out.println("用户名-------------"+username); 
        UserLogin userLogin =  null; 
        if(username != null && !"".equals(username)&& username.indexOf("@") > 0){ 
              userLogin = userLoginDAO.findByEmail(username); 
              username = userLogin.getNick(); 
        }else{ 
            userLogin = userLoginDAO.findByNick(username); 
        } 
        System.out.println("user is null ---"+userLogin.getUserType()); 
        String nick = userLogin.getNick(); 
        String email = userLogin.getEmail(); 
        String mobile = userLogin.getMobile(); 
        int userType = userLogin.getUserType(); 
        List<GrantedAuthority> resultAuths = new ArrayList<GrantedAuthority>(); 
         
 
        // 前台用户 
        if (userType == 1) { 
            resultAuths.add(new SimpleGrantedAuthority("ROLE_USER")); 
        } else { 
            resultAuths.add(new SimpleGrantedAuthority("ROLE_BACK_USER")); 
        } 
         
        return new WrappedUserLogin(userLogin.getId(), email, nick, mobile, userLogin.getPassword(), userType,resultAuths); 
    } 
 

 
3 重写用户名密码验证 
     public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter{ 
        //用户名 
        public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "j_username"; 
        //密码 
        public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "j_password"; 
        //需要回调的URL 自定义参数 
        public static final String SPRING_SECURITY_FORM_REDERICT_KEY = "spring-security-redirect"; 
         
        /** 
         * @deprecated If you want to retain the username, cache it in a customized {@code AuthenticationFailureHandler} 
         */ 
        @Deprecated 
        public static final String SPRING_SECURITY_LAST_USERNAME_KEY = "SPRING_SECURITY_LAST_USERNAME"; 
 
        private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY; 
        private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY; 
        private String redirectParameter = SPRING_SECURITY_FORM_REDERICT_KEY; 
        private boolean postOnly = true; 
 
        //~ Constructors =================================================================================================== 
 
        public MyUsernamePasswordAuthenticationFilter() { 
           super(); 
        } 
 
        //~ Methods ======================================================================================================== 
 
        public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { 
            if (postOnly && !request.getMethod().equals("POST")) { 
                throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod()); 
            } 
            String username = obtainUsername(request); 
            String password = obtainPassword(request); 
            String redirectUrl = obtainRedercitUrl(request); 
            if (username == null) { 
                username = ""; 
            } 
 
            if (password == null) { 
                password = ""; 
            } 
            //自定义回调URL,若存在则放入Session 
            if(redirectUrl != null && !"".equals(redirectUrl)){ 
                request.getSession().setAttribute("callCustomRediretUrl", redirectUrl); 
            } 
            username = username.trim(); 
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); 
            // Allow subclasses to set the "details" property 
            setDetails(request, authRequest); 
            return this.getAuthenticationManager().authenticate(authRequest); 
        } 
 
        /**
         * Enables subclasses to override the composition of the password, such as by including additional values
         * and a separator.<p>This might be used for example if a postcode/zipcode was required in addition to the
         * password. A delimiter such as a pipe (|) should be used to separate the password and extended value(s). The
         * <code>AuthenticationDao</code> will need to generate the expected password in a corresponding manner.</p>
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the password that will be presented in the <code>Authentication</code> request token to the
         *         <code>AuthenticationManager</code>
         */ 
        protected String obtainPassword(HttpServletRequest request) { 
            return request.getParameter(passwordParameter); 
        } 
 
        /**
         * Enables subclasses to override the composition of the username, such as by including additional values
         * and a separator.
         *
         * @param request so that request attributes can be retrieved
         *
         * @return the username that will be presented in the <code>Authentication</code> request token to the
         *         <code>AuthenticationManager</code>
         */ 
        protected String obtainUsername(HttpServletRequest request) { 
            return request.getParameter(usernameParameter); 
        } 
         
         
        protected String obtainRedercitUrl(HttpServletRequest request) { 
            return request.getParameter(redirectParameter); 
        } 
 
        /**
         * Provided so that subclasses may configure what is put into the authentication request's details
         * property.
         *
         * @param request that an authentication request is being created for
         * @param authRequest the authentication request object that should have its details set
         */ 
        protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) { 
            authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); 
        } 
 
        /**
         * Sets the parameter name which will be used to obtain the username from the login request.
         *
         * @param usernameParameter the parameter name. Defaults to "j_username".
         */ 
        public void setUsernameParameter(String usernameParameter) { 
            Assert.hasText(usernameParameter, "Username parameter must not be empty or null"); 
            this.usernameParameter = usernameParameter; 
        } 
 
        /**
         * Sets the parameter name which will be used to obtain the password from the login request..
         *
         * @param passwordParameter the parameter name. Defaults to "j_password".
         */ 
        public void setPasswordParameter(String passwordParameter) { 
            Assert.hasText(passwordParameter, "Password parameter must not be empty or null"); 
            this.passwordParameter = passwordParameter; 
        } 
 
        /**
         * Defines whether only HTTP POST requests will be allowed by this filter.
         * If set to true, and an authentication request is received which is not a POST request, an exception will
         * be raised immediately and authentication will not be attempted. The <tt>unsuccessfulAuthentication()</tt> method
         * will be called as if handling a failed authentication.
         * <p>
         * Defaults to <tt>true</tt> but may be overridden by subclasses.
         */ 
        public void setPostOnly(boolean postOnly) { 
            this.postOnly = postOnly; 
        } 
 
     

 
 
 
4 SimpleUrlAuthenticationSuccessHandler重写 
     public class MySavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler{ 
     @Value(value = "${local.service.url}") 
     private String LOCAL_SERVER_URL; 
     
     protected final Log logger = LogFactory.getLog(this.getClass()); 
 
        private RequestCache requestCache = new HttpSessionRequestCache(); 
 
        @Override 
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, 
                Authentication authentication) throws ServletException, IOException { 
            SavedRequest savedRequest = requestCache.getRequest(request, response); 
            if (savedRequest == null) { 
                System.out.println("savedRequest is null "); 
                //用户判断是否要使用上次通过session里缓存的回调URL地址 
                int flag = 0; 
                //通过提交登录请求传递需要回调的URL callCustomRediretUrl 
                if(request.getSession().getAttribute("callCustomRediretUrl") != null && !"".equals(request.getSession().getAttribute("callCustomRediretUrl"))){ 
                    String url = String.valueOf(request.getSession().getAttribute("callCustomRediretUrl")); 
                    //若session 存在则需要使用自定义回调的URL 而不是缓存的URL 
                    super.setDefaultTargetUrl(url); 
                    super.setAlwaysUseDefaultTargetUrl(true); 
                    flag = 1; 
                    request.getSession().setAttribute("callCustomRediretUrl", ""); 
                } 
                //重设置默认URL为主页地址 
                if(flag  == 0){ 
                    super.setDefaultTargetUrl(LOCAL_SERVER_URL); 
                } 
                super.onAuthenticationSuccess(request, response, authentication); 
                
                return; 
            } 
            //targetUrlParameter 是否存在 
            String targetUrlParameter = getTargetUrlParameter(); 
            if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) { 
                requestCache.removeRequest(request, response); 
                super.setAlwaysUseDefaultTargetUrl(false); 
                super.setDefaultTargetUrl("/"); 
                super.onAuthenticationSuccess(request, response, authentication); 
                return; 
            } 
            //清除属性 
            clearAuthenticationAttributes(request); 
            // Use the DefaultSavedRequest URL 
            String targetUrl = savedRequest.getRedirectUrl(); 
            logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl); 
            if(targetUrl != null && "".equals(targetUrl)){ 
                targetUrl = LOCAL_SERVER_URL; 
            } 
            getRedirectStrategy().sendRedirect(request, response, targetUrl); 
        } 
 
        public void setRequestCache(RequestCache requestCache) { 
            this.requestCache = requestCache; 
        } 

 
5 认证失败控制类重写 
/**
* <tt>AuthenticationFailureHandler</tt> which performs a redirect to the value of the {@link #setDefaultFailureUrl
* defaultFailureUrl} property when the <tt>onAuthenticationFailure</tt> method is called.
* If the property has not been set it will send a 401 response to the client, with the error message from the
* <tt>AuthenticationException</tt> which caused the failure.
* <p>
* If the {@code useForward} property is set, a {@code RequestDispatcher.forward} call will be made to
* the destination instead of a redirect.
*
* @author Luke Taylor
* @since 3.0
*/ 
public class MySimpleUrlAuthenticationFailureHandler implements AuthenticationFailureHandler{ 
 
    protected final Log logger = LogFactory.getLog(getClass()); 
 
    private String defaultFailureUrl; 
    private boolean forwardToDestination = false; 
    private boolean allowSessionCreation = true; 
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); 
    @Value(value = "${local.service.url}") 
    private String LOCAL_SERVER_URL; 
     
    public MySimpleUrlAuthenticationFailureHandler() { 
    } 
 
    public MySimpleUrlAuthenticationFailureHandler(String defaultFailureUrl) { 
        setDefaultFailureUrl(defaultFailureUrl); 
    } 
 
    /**
     * Performs the redirect or forward to the {@code defaultFailureUrl} if set, otherwise returns a 401 error code.
     * <p>
     * If redirecting or forwarding, {@code saveException} will be called to cache the exception for use in
     * the target view.
     */ 
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, 
            AuthenticationException exception) throws IOException, ServletException { 
        //认证失败区别前后台:LOGIN URL 
        if(request.getParameter("spring-security-redirect") != null){ 
              request.getSession().setAttribute("callUrlFailure", request.getParameter("spring-security-redirect")); 
        } 
        //若有loginUrl 则重定向到后台登录界面 
        if(request.getParameter("loginUrl") != null && !"".equals(request.getParameter("loginUrl"))){ 
            defaultFailureUrl = LOCAL_SERVER_URL+"/backlogin.html?validated=false"; 
        } 
        //defaultFailureUrl 默认的认证失败回调URL 
        if (defaultFailureUrl == null) { 
            logger.debug("No failure URL set, sending 401 Unauthorized error"); 
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authentication Failed: " + exception.getMessage()); 
        } else { 
            saveException(request, exception); 
            if (forwardToDestination) { 
                logger.debug("Forwarding to " + defaultFailureUrl); 
                request.getRequestDispatcher(defaultFailureUrl).forward(request, response); 
            } else { 
                logger.debug("Redirecting to " + defaultFailureUrl); 
                redirectStrategy.sendRedirect(request, response, defaultFailureUrl); 
            } 
        } 
    } 
 
    /**
     * Caches the {@code AuthenticationException} for use in view rendering.
     * <p>
     * If {@code forwardToDestination} is set to true, request scope will be used, otherwise it will attempt to store
     * the exception in the session. If there is no session and {@code allowSessionCreation} is {@code true} a session
     * will be created. Otherwise the exception will not be stored.
     */ 
    protected final void saveException(HttpServletRequest request, AuthenticationException exception) { 
        if (forwardToDestination) { 
            request.setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); 
        } else { 
            HttpSession session = request.getSession(false); 
 
            if (session != null || allowSessionCreation) { 
                request.getSession().setAttribute(WebAttributes.AUTHENTICATION_EXCEPTION, exception); 
            } 
        } 
    } 
 
    /**
     * The URL which will be used as the failure destination.
     *
     * @param defaultFailureUrl the failure URL, for example "/loginFailed.jsp".
     */ 
    public void setDefaultFailureUrl(String defaultFailureUrl) { 
        this.defaultFailureUrl = defaultFailureUrl; 
    } 
 
    protected boolean isUseForward() { 
        return forwardToDestination; 
    } 
 
    /**
     * If set to <tt>true</tt>, performs a forward to the failure destination URL instead of a redirect. Defaults to
     * <tt>false</tt>.
     */ 
    public void setUseForward(boolean forwardToDestination) { 
        this.forwardToDestination = forwardToDestination; 
    } 
 
    /**
     * Allows overriding of the behaviour when redirecting to a target URL.
     */ 
    public void setRedirectStrategy(RedirectStrategy redirectStrategy) { 
        this.redirectStrategy = redirectStrategy; 
    } 
 
    protected RedirectStrategy getRedirectStrategy() { 
        return redirectStrategy; 
    } 
 
    protected boolean isAllowSessionCreation() { 
        return allowSessionCreation; 
    } 
 
    public void setAllowSessionCreation(boolean allowSessionCreation) { 
        this.allowSessionCreation = allowSessionCreation; 
    } 
 

 
     6 登录Controller和页面省略 

上一篇: DHCP服务

下一篇: Flask中的CBV