SpringSecurity-2-表单登录流程

SS常见的使用场景之一就是表单登录了,而登录校验主要是经过过滤器来实现的。这里的代码使用了过滤器设计模式,将一系列的过滤器按顺序准备好,依次进行过滤。下面来看下具体的实现流程吧!html

首先看下时序图,直观感觉下:java

稍微细致点的:web

大体说明下:spring

1:最左侧是登录请求达到SS,进入过滤器链FilterChainProxysql

2:被处理登录请求的过滤器AbstractAuthenticationProcessingFilter获取,该过滤器最终的目的是生成一个表明登录用户的权限凭证(Authentication):UsernamePasswordAuthenticationToken数据库

3:过滤器调用AuthenticationManager,权限管理者,它来管理Authentication,设计模式

4:权限管理者的管理方式是经过权限提供者提供的:AbstractUserDetailsAuthenticationProvider,它是用来获取用户及权限的,并判断是否合法api

5:默认的获取用户及权限的方式是经过UserDetailsService接口定义,从命名来看,是为User服务的,因为默认的是从DB获取,所里这里调用的就是其默认的实现类JdbcDaoImpl,即从DB获取安全

6:获取完毕以后,层层返回并判断,最终符合要求就建立一个真正的Authentication,这就表明了登录用户及权限session

下面就来跟源码一块儿分享下,

看看过滤器链的核心:FilterChainProxy

它内部包含一个内部类VirtualFilterChain:

VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
vfc.doFilter(fwRequest, fwResponse);

这里的过滤方法是调用此内部类的doFilter()方法:

public void doFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {
    if (currentPosition == size) {
        if (logger.isDebugEnabled()) {
            logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                + " reached end of additional filter chain; proceeding with original chain");
        }
        // Deactivate path stripping as we exit the security filter chain
        this.firewalledRequest.reset();
            originalChain.doFilter(request, response);
    }
    else {
        currentPosition++;
        Filter nextFilter = additionalFilters.get(currentPosition - 1);
        if (logger.isDebugEnabled()) {
        logger.debug(UrlUtils.buildRequestUrl(firewalledRequest)
                + " at position " + currentPosition + " of " + size
                + " in additional filter chain; firing Filter: '"
                + nextFilter.getClass().getSimpleName() + "'");
        }
        nextFilter.doFilter(request, response, this);
    }
}

能够看到,currentPosition表明当前处理的过滤器链List additionalFilters的索引,由第一个开始,若未到达最后一个过滤器,则currentPosition++,继续处理

debug看到过滤器链以下:

[org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@5562047f,
 org.springframework.security.web.context.SecurityContextPersistenceFilter@53956e2d,
 org.springframework.security.web.header.HeaderWriterFilter@2477dc48,
 org.springframework.security.web.authentication.logout.LogoutFilter@170b08a9,
 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@aacdd0c,
 org.springframework.security.web.authentication.www.BasicAuthenticationFilter@4e049f68,
 org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2aaa4fde,
 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@13079b81,
 org.springframework.security.web.authentication.AnonymousAuthenticationFilter@394af762,
 org.springframework.security.web.session.SessionManagementFilter@3c3b6e37,
 org.springframework.security.web.access.ExceptionTranslationFilter@6567439d,
 org.springframework.security.web.access.intercept.FilterSecurityInterceptor@5edb1d1f]

其中,这里关注的是:UsernamePasswordAuthenticationFilter

这里调用它的父类AbstractAuthenticationProcessingFilter的doFilter():

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        if (!requiresAuthentication(request, response)) {
            chain.doFilter(request, response);
            return;
        }
        if (logger.isDebugEnabled()) {
            logger.debug("Request is to process authentication");
        }
        Authentication authResult;

        try {
            //调用权限验证,返回的是Authentication对象
            authResult = attemptAuthentication(request, response);
            if (authResult == null) {
                // return immediately as subclass has indicated that it hasn't completed
                // authentication
                return;
            }
            sessionStrategy.onAuthentication(authResult, request, response);
        }
        catch (InternalAuthenticationServiceException failed) {
            logger.error(
                    "An internal error occurred while trying to authenticate the user.",
                    failed);
            unsuccessfulAuthentication(request, response, failed);

            return;
        }
        catch (AuthenticationException failed) {
            // Authentication failed
            unsuccessfulAuthentication(request, response, failed);
            return;
        }
        // Authentication success
        if (continueChainBeforeSuccessfulAuthentication) {
            chain.doFilter(request, response);
        }

        successfulAuthentication(request, response, chain, authResult);
    }

主要调用的是authResult = attemptAuthentication(request, response)方法,该方法定义:

public abstract Authentication attemptAuthentication

该类是须要被子类重写的,也就是UsernamePasswordAuthenticationFilter的attemptAuthentication:

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);
    if (username == null) {
        username = "";
    }
    if (password == null) {
        password = "";
    }
    username = username.trim();
    //建立一个Authentication实例
    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
            username, password);
    // Allow subclasses to set the "details" property
    setDetails(request, authRequest);
    //获取AuthenticationManager,这里就是Spring启动的时候new出来的ProviderManager
    return this.getAuthenticationManager().authenticate(authRequest);
}

这里的UsernamePasswordAuthenticationToken是一个权限的凭证:

包括了用户名,密码,ip,sessionId,是否已受权过一级权限集合。

继续调用ProviderManager的authenticate()方法,以下:

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();
        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }

            try {
                //调用provider的authenticate,这里能够自定义扩展
                result = provider.authenticate(authentication);
                if (result != null) {
                    copyDetails(authentication, result);
                    break;
                }
            }
            ...
        }

        if (result == null && parent != null) {
            // Allow the parent to try.
            try {
                result = parent.authenticate(authentication);
            } catch (AuthenticationException e) {
                lastException = e;
            }
        }
        if (result != null) {
            //认证成功,清除密码等信息
            if (eraseCredentialsAfterAuthentication
                    && (result instanceof CredentialsContainer)) {
                ((CredentialsContainer) result).eraseCredentials();
            }
            eventPublisher.publishAuthenticationSuccess(result);
            return result;
        }
        prepareException(lastException, authentication);
        throw lastException;
    }

经过判断发现,调用了result = parent.authenticate(authentication);即ProviderManager自己,它包含的provider为[org.springframework.security.authentication.dao.DaoAuthenticationProvider],这里是能够自定义扩展的

这里调用的是DaoAuthenticationProvider父类AbstractUserDetailsAuthenticationProvider的authenticate()方法:

public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {

        // Determine username
        String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
                : authentication.getName();

        boolean cacheWasUsed = true;
        UserDetails user = this.userCache.getUserFromCache(username);

        if (user == null) {
            cacheWasUsed = false;
            try { 
                //调用DaoAuthenticationProvider的获取用户方法 
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            catch (UsernameNotFoundException notFound) {
                //看到这里的异常转换,统一转换成了BadCredentialsException
                //能够经过在子类中重写父类的属性去掉转换:super.setHideUserNotFoundExceptions(false)
                logger.debug("User '" + username + "' not found");
                if (hideUserNotFoundExceptions) {
                    throw new BadCredentialsException(messages.getMessage(
                            "AbstractUserDetailsAuthenticationProvider.badCredentials",
                            "Bad credentials"));
                }
                else {
                    throw notFound;
                }
            }

            Assert.notNull(user,
                    "retrieveUser returned null - a violation of the interface contract");
        }

        try {
            //这里已经获取到DB中的用户,判断用户是否锁定,启用,过时
            preAuthenticationChecks.check(user);
            //调用DaoAuthenticationProvider,判断用户的密码是否正确
            additionalAuthenticationChecks(user,
                    (UsernamePasswordAuthenticationToken) authentication);
        }
        catch (AuthenticationException exception) {
            if (cacheWasUsed) {
                // There was a problem, so try again after checking
                // we're using latest data (i.e. not from the cache)
                cacheWasUsed = false;
                user = retrieveUser(username,
                        (UsernamePasswordAuthenticationToken) authentication);
                preAuthenticationChecks.check(user);
                additionalAuthenticationChecks(user,
                        (UsernamePasswordAuthenticationToken) authentication);
            }
            else {
                throw exception;
            }
        }

        postAuthenticationChecks.check(user);

        if (!cacheWasUsed) {
            this.userCache.putUserInCache(user);
        }

        Object principalToReturn = user;

        if (forcePrincipalAsString) {
            principalToReturn = user.getUsername();
        }

        return createSuccessAuthentication(principalToReturn, authentication, user);
    }

这里调用了DaoAuthenticationProvider的retrieveUser()方法,返回一个UserDetail实例

UserDetails loadedUser;

try {
    //获取UserDetailService,能够扩展为本身的获取用户的方法
    //要注意这里仅仅传入了username,获取到用户以后难以进行其余操做了,返回的用户就是SS框架中使用的用户了
    loadedUser = this.getUserDetailsService().loadUserByUsername(username);
} catch (UsernameNotFoundException notFound) {
    if (authentication.getCredentials() != null) {
        String presentedPassword = authentication.getCredentials().toString();
        passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,
                presentedPassword, null);
    }
    throw notFound;
}

这里看到这里是调用了UserDetailsService实现类,若是没有扩展,这默认使用JdbcDaoImpl,它同时也继承了JdbcDaoSupport,基本是基于Spring Data JPA实现的DB查询:

public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {
    List<UserDetails> users = loadUsersByUsername(username);

    if (users.size() == 0) {
        logger.debug("Query returned no results for user '" + username + "'");
        throw new UsernameNotFoundException(messages.getMessage(
                "JdbcDaoImpl.notFound", new Object[] { username },
                "Username {0} not found"));
    }
    UserDetails user = users.get(0); // contains no GrantedAuthority[]
    Set<GrantedAuthority> dbAuthsSet = new HashSet<GrantedAuthority>();

    if (enableAuthorities) {
        //从DB中将该用户相关联的[权限]查询出来并存储
        dbAuthsSet.addAll(loadUserAuthorities(user.getUsername()));
    }
    if (enableGroups) {
        //从DB中将该用户相关联的[组权限]查询出来并存储
        dbAuthsSet.addAll(loadGroupAuthorities(user.getUsername()));
    }
    List<GrantedAuthority> dbAuths = new ArrayList<GrantedAuthority>(dbAuthsSet);
    //添加自定义的权限,须要子类本身实现
    addCustomAuthorities(user.getUsername(), dbAuths);
    if (dbAuths.size() == 0) {
        logger.debug("User '" + username
                + "' has no authorities and will be treated as 'not found'");

        throw new UsernameNotFoundException(messages.getMessage(
                "JdbcDaoImpl.noAuthority", new Object[] { username },
                "User {0} has no GrantedAuthority"));
    }
    //new出一个UserDetail实例
    return createUserDetails(username, user, dbAuths);
}

通常状况下能够扩展UserDetailsService,实现本身的获取用户,权限,建立用户的逻辑,该方法只是须要返回一个UserDetail实例

看下JdbcDaoImpl查询用户方法:

protected List<UserDetails> loadUsersByUsername(String username) {
    return getJdbcTemplate().query(usersByUsernameQuery, new String[] { username },
            new RowMapper<UserDetails>() {
                public UserDetails mapRow(ResultSet rs, int rowNum)
                        throws SQLException {
                    String username = rs.getString(1);
                    String password = rs.getString(2);
                    boolean enabled = rs.getBoolean(3);
                    return new User(username, password, enabled, true, true, true,
                            AuthorityUtils.NO_AUTHORITIES);
                }

            });
}

其中查询用户的sql已在class中指定了:

public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled "
            + "from users " + "where username = ?";

这里默认从DB中获取到了一个用户以下:

通过最后的createUserDetails(username, user, dbAuths)返回一个全新的用户实例:

能够看到,查询出的权限已经被set到了用户属性中

返回用户以后,继续回到AbstractUserDetailsAuthenticationProvider的authenticate()方法,上面源码中已标注了继续进行用户判断,包括是否启用,锁定,过时:

preAuthenticationChecks.check(user)

能够看到用户属性全都是true,校验经过(建立UserDetail时默认的都是true)!

继续判断密码是否正确:

additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication)

这个方法的定义:protected abstract void additionalAuthenticationChecks

仍然是须要被子类继承重写的

因为这里没有扩展DaoAuthenticationProvider类,因此这里就调用了子类DaoAuthenticationProvider的方法:

protected void additionalAuthenticationChecks(UserDetails userDetails,
        UsernamePasswordAuthenticationToken authentication)
        throws AuthenticationException {
    Object salt = null;
    //盐值
    if (this.saltSource != null) {
        salt = this.saltSource.getSalt(userDetails);
    }

    if (authentication.getCredentials() == null) {
        logger.debug("Authentication failed: no credentials provided");

        throw new BadCredentialsException(messages.getMessage(
                "AbstractUserDetailsAuthenticationProvider.badCredentials",
                "Bad credentials"));
    }

    String presentedPassword = authentication.getCredentials().toString();
    //判断密码是否有效
    if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
            presentedPassword, salt)) {
        logger.debug("Authentication failed: password does not match stored value");

        throw new BadCredentialsException(messages.getMessage(
                "AbstractUserDetailsAuthenticationProvider.badCredentials",
                "Bad credentials"));
    }
}

这里没有重写PasswordEncoder,SS使用了默认的加解密,最终经过:

BCrypt.checkpw(rawPassword.toString(), encodedPassword)实现

校验经过

走到这里,AbstractUserDetailsAuthenticationProvider的authenticate()方法即将执行结束,用户,权限已经查询出来了,属性,密码等也校验了,可是该方法要返回一个Authentication对象,这里还未执行。

由前所述,在最开始的UsernamePasswordAuthenticationFilter中已经经过登陆的用户名,密码等信息建立一个Authentication:UsernamePasswordAuthenticationToken,可是这个权限对象是未通过权限认证的,是不可靠的,因此要对它进行更新,也就是最后一句:

return createSuccessAuthentication(principalToReturn, authentication, user);

protected Authentication createSuccessAuthentication(Object principal,
        Authentication authentication, UserDetails user) {
    // Ensure we return the original credentials the user supplied,
    // so subsequent attempts are successful even with encoded passwords.
    // Also ensure we return the original getDetails(), so that future
    // authentication events after cache expiry contain the details
    UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
            principal, authentication.getCredentials(),
            authoritiesMapper.mapAuthorities(user.getAuthorities()));
    result.setDetails(authentication.getDetails());

    return result;
}

这里就是经过查询出的信息从新建立了一个Authentication:UsernamePasswordAuthenticationToken对象,并返回!

至此,在ProviderManager中的权限校验方法已经执行完毕,SS框架已经拿到了真实的登录用户了

而后执行密码等凭证清除方法,保证安全,最后进行认证成功的事件发布

走到这里,过滤器链基本就执行完毕了,后续继续进行doFilter以后的方法了

--------------------

我这边也简单的扩展了常见类,用于实现一个权限API接口验证:

首先是启动配置类:

@Order(99)
  @Profile("auth")
  @Configuration
  @EnableWebSecurity
  @EnableGlobalMethodSecurity(prePostEnabled = true)
  static class SpringSecurityConfigurer extends WebSecurityConfigurerAdapter {

	@Autowired
	private JdbcUserDetailsManager userDetailsManager;
	@Autowired
	private UserRepository userRepository; 
	@Value("${umUrl}")
	private String umUrl;
	  
    public static final String USER_ROLE = "user";

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    	
      UmAuthenticationFailureHandler failHandler = new UmAuthenticationFailureHandler();
    	
      http.csrf().disable();
      http.headers().frameOptions().sameOrigin();
      http.authorizeRequests()
      		.antMatchers("/openapi/**", "/vendor/**", "/styles/**", "/scripts/**", "/views/**", "/img/**")
      		.permitAll()
      		.antMatchers("/**")
      		.hasAnyRole(USER_ROLE);
      http.formLogin()
      		.loginPage("/signin")
      		.permitAll()
      		.failureHandler(failHandler)
//      		.failureUrl("/signin?#/error")
      		.and()
      		.httpBasic();
      http.logout()
      		.invalidateHttpSession(true)
      		.clearAuthentication(true)
      		.logoutSuccessUrl("/signin?#/logout");
      http.exceptionHandling()
      		.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/signin"));
      
      System.out.println(umUrl);
      
      //扩展登录校验
      //userService
      UmUserDetailsService userService = new UmUserDetailsService(userDetailsManager,userRepository);
      
      //provider
      AuthenticationProvider umDaoAuthenticationProvider = new UmDaoAuthenticationProvider(userService,umUrl);
//      AuthenticationProvider umDaoAuthenticationProvider = new UmDaoAuthenticationProvider(userService,userDetailsManager);
      List<AuthenticationProvider> providers = new ArrayList<AuthenticationProvider>();
      providers.add(umDaoAuthenticationProvider);
      
      //authManager
//      UmAuthenticationManager mamager = new UmAuthenticationManager(providers);
//      UmAuthenticationManager umAuthenticationManager = new UmAuthenticationManager(providers,mamager);
      UmAuthenticationManager umAuthenticationManager = new UmAuthenticationManager(providers);
      
      //filter
      UmUserPwdAuthenticationFilter umUserPwdAuthenticationFilter = new UmUserPwdAuthenticationFilter(umAuthenticationManager);

      //add filter to chain
      http.authenticationProvider(umDaoAuthenticationProvider).addFilterAfter(umUserPwdAuthenticationFilter,UmUserPwdAuthenticationFilter.class);
      
    }
  }

该类里面主要是新增的校验扩展类,最终造成一个过滤器并加入到过滤器链中

相关类以下:

public class UmUserPwdAuthenticationFilter extends UsernamePasswordAuthenticationFilter{
	AuthenticationManager umAuthenticationManager = null;
	public UmUserPwdAuthenticationFilter (UmAuthenticationManager umAuthenticationManager) {
		this.umAuthenticationManager = umAuthenticationManager;
		setAuthenticationManager(umAuthenticationManager);
	}
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
			throws AuthenticationException {
		System.out.println("--------------------MyUserPwdAuthenticationProvider-------------");
		return super.attemptAuthentication(request, response);
	}
}
public class UmAuthenticationManager extends ProviderManager{
	
	public UmAuthenticationManager(List<AuthenticationProvider> providers) {
		super(providers);
	}
	public UmAuthenticationManager(List<AuthenticationProvider> providers,UmAuthenticationManager umAuthenticationManager) {
		super(providers,umAuthenticationManager);
	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		System.out.println("------------------------UmAuthenticationManager---------------------------");
		return authentication;
	}
	
}
@Service
public class UmUserDetailsService implements UserDetailsService {

	protected final Log logger = LogFactory.getLog(getClass());

	private JdbcUserDetailsManager userDetailsManager;
	private UserRepository userRepository;

	public UmUserDetailsService() {
	}

	public UmUserDetailsService(JdbcUserDetailsManager userDetailsManager, UserRepository userRepository) {
		this.userDetailsManager = userDetailsManager;
		this.userRepository = userRepository;
	}

	/**
	 * 原本能够调用框架的JdbcDaoImpl#loadUserByUsername从数据库获取用户,
	 * 可是若是登录用户没有存储到DB,那么获取到的用户为null,后续框架鉴权不过,就走不到UM鉴权
	 * 因此重写了loadUserByUsername方法,考虑不在这里进行DB查询
	 * 但又因为这里重写的方法参数只有username,没有password,没法进行UM鉴权,只能考虑往前重写父类方法 可是protected final
	 * UserDetails retrieveUser,是没法继承重写的 因此这里只能新建一个非空校验用户UserDetail,用以跨过框架的校验
	 * 后续发现跨过框架校验后,没法将DB查出的用户替换掉校验用户,因此只能在这里查询DB用户
	 */
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

		logger.info("UmUserDetailsService#loadUserByUsername :username = " + username);

		// 查询当前用户是否存在于DB
		String usersByUsernameQuery = "select id,username,email,enabled from users where username = ?";
		List<UserInfo> userList = userDetailsManager.getJdbcTemplate().query(usersByUsernameQuery,
				new String[] { username }, new RowMapper<UserInfo>() {
					public UserInfo mapRow(ResultSet rs, int rowNum) throws SQLException {
						int userId = rs.getInt(1);
						String username = rs.getString(2);
						String email = rs.getString(3);
						boolean enabled = rs.getBoolean(4);
						return new UserInfo(userId + "", username, email, enabled ? "1" : "0");
					}
				});

		// user exists
		if (userList != null && userList.size() > 0) {
			logger.info("UmUserDetailsService#loadUserByUsername get username from db success :username = " + username);
			return userList.get(0);
		} else {
			throw new InternalAuthenticationServiceException("NO_REGISTER:还没有开通配置平台帐号,请联系ZHUANGJIAJIE778开通");			
		}
	}

	public JdbcUserDetailsManager getUserDetailsManager() {
		return userDetailsManager;
	}

	public UserRepository getUserRepository() {
		return userRepository;
	}

}
public class UmDaoAuthenticationProvider extends DaoAuthenticationProvider{

	private PasswordEncoder encoder = new BCryptPasswordEncoder();
	private JdbcUserDetailsManager userDetailsManager;
	private String umUrl;
	
	public UmDaoAuthenticationProvider(UmUserDetailsService umUserDetailsService,String umUrl) {
		super.setHideUserNotFoundExceptions(false);		//禁止包装异常
		this.umUrl = umUrl;
		this.userDetailsManager = userDetailsManager;
		setUserDetailsService(umUserDetailsService);
	}
	
//	public UmDaoAuthenticationProvider(UmUserDetailsService umUserDetailsService,JdbcUserDetailsManager userDetailsManager) {
//		super.setHideUserNotFoundExceptions(false);
//		this.umUserDetailsService = umUserDetailsService;
//		this.userDetailsManager = userDetailsManager;
//		setUserDetailsService(umUserDetailsService);
//	}

	@SuppressWarnings("deprecation")
	protected void additionalAuthenticationChecks(UserDetails userDetails,
			UsernamePasswordAuthenticationToken authentication)
			throws AuthenticationException {
		Object salt = null;

//		if (this.saltSource != null) {
//			salt = this.saltSource.getSalt(userDetails);
//		}

		if (authentication.getCredentials() == null) {
			logger.debug("Authentication failed: no credentials provided");

			throw new BadCredentialsException(messages.getMessage(
					"AbstractUserDetailsAuthenticationProvider.badCredentials",
					"Bad credentials"));
		}

		String username = authentication.getPrincipal().toString();
		String presentedPassword = authentication.getCredentials().toString();
		logger.info("MyDaoAuthenticationProvider # additionalAuthenticationChecks : pppp = " + presentedPassword);

		if(username.equals("apollo")) {
			if("apolloadmin".equals(presentedPassword)) {
				logger.error("login success : no need to UM authenticate user : [username = " + username + "] : ");
				return;
			} else {
				logger.error("login fail : pwd error : [username = " + username + "] : ");
				throw new InternalAuthenticationServiceException("login fail[username = " + username + " ] : username or pwd error!");
			}
//		} else if(username.equals("yangli1")) {
//			logger.error("UM鉴权失败[username = " + username + "] : ");
//			throw new InternalAuthenticationServiceException("用户名或者密码错误---自定义");
		}
//		logger.info("UM鉴权成功[username = " + username + "]");
		
		//开始UM鉴权
		String URL_INFO 	 = umUrl + "/common/checkUUU.shtml";
//		String URL_INFO 	 = "http://localhost:8888/smp-cms-web/common/checkUmUser.shtml";
		logger.info("UM authenticate url : [URL_INFO = " + URL_INFO + "]");
		
		NameValuePair[] data = {new NameValuePair("uuu", authentication.getPrincipal().toString()),
								new NameValuePair("ppp", presentedPassword)};
		try {
			Map<String, Object> resultMap = executeHttp(URL_INFO,data);
			if(resultMap.get("resultCode") != null && resultMap.get("resultCode").equals("00")) {
				logger.info("UM authenticate success : [username = " + username + "]");
			} else {
				logger.error("UM authenticate fail : [username = " + username + "]");
				throw new InternalAuthenticationServiceException("UM authenticate fail : [username = " + username + " ] : 用户名或者密码错误!");
			}
		} catch (Exception e) {
			logger.error("UM authenticate fail : [username = " + username + "] : " + e.getMessage(),e);
			throw new InternalAuthenticationServiceException("UM authenticate fail : [username = " + username + " ] :"
					+ "	invoke um authenticate interface error : " + e.getMessage(),e);
		}
		
//		if (!passwordEncoder.isPasswordValid(userDetails.getPassword(),
//				presentedPassword, salt)) {
//			logger.debug("Authentication failed: password does not match stored value");
//
//			throw new BadCredentialsException(messages.getMessage(
//					"AbstractUserDetailsAuthenticationProvider.badCredentials",
//					"Bad credentials"));
//		}
		
	}
	
	protected Map<String, Object> executeHttp(String URL, NameValuePair[] data) throws Exception {
		Map<String, Object> resultMap = new HashMap<String, Object>();
		logger.info("UmDaoAuthenticationProvider#executeHttp#url:[" + JSON.toJSONString(data) + "]");
		logger.info("UmDaoAuthenticationProvider#executeHttp#address:[" + URL + "]");
		String resBodyStr = ""; 
		int code = 0;
		try {
			HttpClient httpClient = new HttpClient();
			PostMethod postMethod = new PostMethod(URL);
			postMethod.setRequestBody(data);
			postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "UTF-8");
			httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(10000);
			httpClient.getHttpConnectionManager().getParams().setSoTimeout(10000);
			code = httpClient.executeMethod(postMethod);// 执行postMethod,返回状态码
			resBodyStr = postMethod.getResponseBodyAsString();
			logger.info("UmDaoAuthenticationProvider#executeHttp#responseCode:[" + code + "]#responseMsg:[:" + resBodyStr + "]");

			// 状态码为200时,成功请求,其中返回CODE非00,均表示不正常
			if (code == 200 && resBodyStr != null) {
				Map<String, Object> map = JSON.parseObject(resBodyStr, Map.class);
				resultMap.put("resultCode", map.get("CODE"));
				resultMap.put("resultMsg", map.get("MSG"));
				if (map.get("CODE").equals("00")) {
					resultMap.put("resultData", map.get("DATA"));
				}
			} else {
				resultMap.put("resultCode", "01");
				resultMap.put("resultMsg", "invoke um authenticate interface error , contact ITer plz");
			}
		} catch (Exception e) {
			e.printStackTrace();
			logger.error("UmDaoAuthenticationProvider#executeHttp#exception#code[" + code + "]#resBodyStr[:" + resBodyStr + "]" + e.getMessage(),e);
			resultMap.put("resultCode", "01");
			resultMap.put("resultMsg", "invoke um authenticate interface error , contact ITer plz");
		}
		return resultMap;
	}

	
}
public class UmAuthenticationFailureHandler implements AuthenticationFailureHandler{

	protected final Log logger = LogFactory.getLog(getClass());
	
	private String defaultFailureUrl = "/signin?#/error";
	private static String NO_REGISTER = "NO_REGISTER";
	private boolean forwardToDestination = false;
	private boolean allowSessionCreation = true;
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

	/**
	 * 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 {

		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);
			
			//初始化
			defaultFailureUrl = "/signin?#/error";
			
			//未开通帐号的错误
			if(exception.getMessage().startsWith(NO_REGISTER)) {
				defaultFailureUrl = defaultFailureUrl + "="+ NO_REGISTER;
			}

			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);
			}
		}
	}

}

实现了功能,可是还没优化,能够参考下

相关文章
相关标签/搜索