绑定设备登录的实现

来源:互联网 发布:crossover16破解linux 编辑:程序博客网 时间:2024/06/15 00:55

常规的登录方式是用户名,密码登录。现在项目 中有这样的 需求:要求一台移动设备(手机或者平板)在不输入用户名和密码的情况下,也可以登录成功,前提是这台设备已经唯一绑定了一个用户,并发送了设备的相关信息。

要想加入自己的认证方式,就必须要了解Spring security 是怎样实现用户认证的。

先来了解下框架中的几个类:

1.UserDetails 接口  :提供核心的用户信息

 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limitedpackage org.springframework.security.core.userdetails;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;import java.io.Serializable;import java.util.Collection;/** * Provides core user information. * * <p> * Implementations are not used directly by Spring Security for security purposes. They * simply store user information which is later encapsulated into {@link Authentication} * objects. This allows non-security related user information (such as email addresses, * telephone numbers etc) to be stored in a convenient location. * <p> * Concrete implementations must take particular care to ensure the non-null contract * detailed for each method is enforced. See * {@link org.springframework.security.core.userdetails.User} for a reference * implementation (which you might like to extend or use in your code). * * @see UserDetailsService * @see UserCache * * @author Ben Alex */public interface UserDetails extends Serializable {// ~ Methods// ========================================================================================================/** * Returns the authorities granted to the user. Cannot return <code>null</code>. * * @return the authorities, sorted by natural key (never <code>null</code>) */Collection<? extends GrantedAuthority> getAuthorities();/** * Returns the password used to authenticate the user. * * @return the password */String getPassword();/** * Returns the username used to authenticate the user. Cannot return <code>null</code> * . * * @return the username (never <code>null</code>) */String getUsername();/** * Indicates whether the user's account has expired. An expired account cannot be * authenticated. * * @return <code>true</code> if the user's account is valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */boolean isAccountNonExpired();/** * Indicates whether the user is locked or unlocked. A locked user cannot be * authenticated. * * @return <code>true</code> if the user is not locked, <code>false</code> otherwise */boolean isAccountNonLocked();/** * Indicates whether the user's credentials (password) has expired. Expired * credentials prevent authentication. * * @return <code>true</code> if the user's credentials are valid (ie non-expired), * <code>false</code> if no longer valid (ie expired) */boolean isCredentialsNonExpired();/** * Indicates whether the user is enabled or disabled. A disabled user cannot be * authenticated. * * @return <code>true</code> if the user is enabled, <code>false</code> otherwise */boolean isEnabled();}

 2. User: UserDetails最直接的一个实现,该实现只要找到对应的用户名就算认证成功

 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limitedpackage org.springframework.security.core.userdetails;import java.io.Serializable;import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.List;import java.util.Set;import java.util.SortedSet;import java.util.TreeSet;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.CredentialsContainer;import org.springframework.security.core.SpringSecurityCoreVersion;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.util.Assert;/** * Models core user information retrieved by a {@link UserDetailsService}. * <p> * Developers may use this class directly, subclass it, or write their own * {@link UserDetails} implementation from scratch. * <p> * {@code equals} and {@code hashcode} implementations are based on the {@code username} * property only, as the intention is that lookups of the same user principal object (in a * user registry, for example) will match where the objects represent the same user, not * just when all the properties (authorities, password for example) are the same. * <p> * Note that this implementation is not immutable. It implements the * {@code CredentialsContainer} interface, in order to allow the password to be erased * after authentication. This may cause side-effects if you are storing instances * in-memory and reusing them. If so, make sure you return a copy from your * {@code UserDetailsService} each time it is invoked. * * @author Ben Alex * @author Luke Taylor */public class User implements UserDetails, CredentialsContainer {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;// ~ Instance fields// ================================================================================================private String password;private final String username;private final Set<GrantedAuthority> authorities;private final boolean accountNonExpired;private final boolean accountNonLocked;private final boolean credentialsNonExpired;private final boolean enabled;// ~ Constructors// ===================================================================================================/** * Calls the more complex constructor with all boolean arguments set to {@code true}. */public User(String username, String password,Collection<? extends GrantedAuthority> authorities) {this(username, password, true, true, true, true, authorities);}/** * Construct the <code>User</code> with the details required by * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider}. * * @param username the username presented to the * <code>DaoAuthenticationProvider</code> * @param password the password that should be presented to the * <code>DaoAuthenticationProvider</code> * @param enabled set to <code>true</code> if the user is enabled * @param accountNonExpired set to <code>true</code> if the account has not expired * @param credentialsNonExpired set to <code>true</code> if the credentials have not * expired * @param accountNonLocked set to <code>true</code> if the account is not locked * @param authorities the authorities that should be granted to the caller if they * presented the correct username and password and the user is enabled. Not null. * * @throws IllegalArgumentException if a <code>null</code> value was passed either as * a parameter or as an element in the <code>GrantedAuthority</code> collection */public User(String username, String password, boolean enabled,boolean accountNonExpired, boolean credentialsNonExpired,boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {if (((username == null) || "".equals(username)) || (password == null)) {throw new IllegalArgumentException("Cannot pass null or empty values to constructor");}this.username = username;this.password = password;this.enabled = enabled;this.accountNonExpired = accountNonExpired;this.credentialsNonExpired = credentialsNonExpired;this.accountNonLocked = accountNonLocked;this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));}// ~ Methods// ========================================================================================================public Collection<GrantedAuthority> getAuthorities() {return authorities;}public String getPassword() {return password;}public String getUsername() {return username;}public boolean isEnabled() {return enabled;}public boolean isAccountNonExpired() {return accountNonExpired;}public boolean isAccountNonLocked() {return accountNonLocked;}public boolean isCredentialsNonExpired() {return credentialsNonExpired;}public void eraseCredentials() {password = null;}private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");// Ensure array iteration order is predictable (as per// UserDetails.getAuthorities() contract and SEC-717)SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet<GrantedAuthority>(new AuthorityComparator());for (GrantedAuthority grantedAuthority : authorities) {Assert.notNull(grantedAuthority,"GrantedAuthority list cannot contain any null elements");sortedAuthorities.add(grantedAuthority);}return sortedAuthorities;}private static class AuthorityComparator implements Comparator<GrantedAuthority>,Serializable {private static final long serialVersionUID = SpringSecurityCoreVersion.SERIAL_VERSION_UID;public int compare(GrantedAuthority g1, GrantedAuthority g2) {// Neither should ever be null as each entry is checked before adding it to// the set.// If the authority is null, it is a custom authority and should precede// others.if (g2.getAuthority() == null) {return -1;}if (g1.getAuthority() == null) {return 1;}return g1.getAuthority().compareTo(g2.getAuthority());}}/** * Returns {@code true} if the supplied object is a {@code User} instance with the * same {@code username} value. * <p> * In other words, the objects are equal if they have the same username, representing * the same principal. */@Overridepublic boolean equals(Object rhs) {if (rhs instanceof User) {return username.equals(((User) rhs).username);}return false;}/** * Returns the hashcode of the {@code username}. */@Overridepublic int hashCode() {return username.hashCode();}@Overridepublic String toString() {StringBuilder sb = new StringBuilder();sb.append(super.toString()).append(": ");sb.append("Username: ").append(this.username).append("; ");sb.append("Password: [PROTECTED]; ");sb.append("Enabled: ").append(this.enabled).append("; ");sb.append("AccountNonExpired: ").append(this.accountNonExpired).append("; ");sb.append("credentialsNonExpired: ").append(this.credentialsNonExpired).append("; ");sb.append("AccountNonLocked: ").append(this.accountNonLocked).append("; ");if (!authorities.isEmpty()) {sb.append("Granted Authorities: ");boolean first = true;for (GrantedAuthority auth : authorities) {if (!first) {sb.append(",");}first = false;sb.append(auth);}}else {sb.append("Not granted any authorities");}return sb.toString();}public static UserBuilder withUsername(String username) {return new UserBuilder().username(username);}/** * Builds the user to be added. At minimum the username, password, and authorities * should provided. The remaining attributes have reasonable defaults. */public static class UserBuilder {private String username;private String password;private List<GrantedAuthority> authorities;private boolean accountExpired;private boolean accountLocked;private boolean credentialsExpired;private boolean disabled;/** * Creates a new instance */private UserBuilder() {}/** * Populates the username. This attribute is required. * * @param username the username. Cannot be null. * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */private UserBuilder username(String username) {Assert.notNull(username, "username cannot be null");this.username = username;return this;}/** * Populates the password. This attribute is required. * * @param password the password. Cannot be null. * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder password(String password) {Assert.notNull(password, "password cannot be null");this.password = password;return this;}/** * Populates the roles. This method is a shortcut for calling * {@link #authorities(String...)}, but automatically prefixes each entry with * "ROLE_". This means the following: * * <code> *     builder.roles("USER","ADMIN"); * </code> * * is equivalent to * * <code> *     builder.authorities("ROLE_USER","ROLE_ADMIN"); * </code> * * <p> * This attribute is required, but can also be populated with * {@link #authorities(String...)}. * </p> * * @param roles the roles for this user (i.e. USER, ADMIN, etc). Cannot be null, * contain null values or start with "ROLE_" * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder roles(String... roles) {List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>(roles.length);for (String role : roles) {Assert.isTrue(!role.startsWith("ROLE_"), role+ " cannot start with ROLE_ (it is automatically added)");authorities.add(new SimpleGrantedAuthority("ROLE_" + role));}return authorities(authorities);}/** * Populates the authorities. This attribute is required. * * @param authorities the authorities for this user. Cannot be null, or contain * null values * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) * @see #roles(String...) */public UserBuilder authorities(GrantedAuthority... authorities) {return authorities(Arrays.asList(authorities));}/** * Populates the authorities. This attribute is required. * * @param authorities the authorities for this user. Cannot be null, or contain * null values * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) * @see #roles(String...) */public UserBuilder authorities(List<? extends GrantedAuthority> authorities) {this.authorities = new ArrayList<GrantedAuthority>(authorities);return this;}/** * Populates the authorities. This attribute is required. * * @param authorities the authorities for this user (i.e. ROLE_USER, ROLE_ADMIN, * etc). Cannot be null, or contain null values * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) * @see #roles(String...) */public UserBuilder authorities(String... authorities) {return authorities(AuthorityUtils.createAuthorityList(authorities));}/** * Defines if the account is expired or not. Default is false. * * @param accountExpired true if the account is expired, false otherwise * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder accountExpired(boolean accountExpired) {this.accountExpired = accountExpired;return this;}/** * Defines if the account is locked or not. Default is false. * * @param accountLocked true if the account is locked, false otherwise * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder accountLocked(boolean accountLocked) {this.accountLocked = accountLocked;return this;}/** * Defines if the credentials are expired or not. Default is false. * * @param credentialsExpired true if the credentials are expired, false otherwise * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder credentialsExpired(boolean credentialsExpired) {this.credentialsExpired = credentialsExpired;return this;}/** * Defines if the account is disabled or not. Default is false. * * @param disabled true if the account is disabled, false otherwise * @return the {@link UserBuilder} for method chaining (i.e. to populate * additional attributes for this user) */public UserBuilder disabled(boolean disabled) {this.disabled = disabled;return this;}public UserDetails build() {return new User(username, password, !disabled, !accountExpired,!credentialsExpired, !accountLocked, authorities);}}}

3.AbstractUserDetailsAuthenticationProvider:用来认证UserDetails 是否正确,提供了基本的认证方法。

public abstract class AbstractUserDetailsAuthenticationProvider implementsAuthenticationProvider, InitializingBean, MessageSourceAware {
public Authentication authenticate(Authentication authentication)throws AuthenticationException {Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports","Only UsernamePasswordAuthenticationToken is supported"));// Determine usernameString username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED": authentication.getName();boolean cacheWasUsed = true;UserDetails user = this.userCache.getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {user = retrieveUser(username,(UsernamePasswordAuthenticationToken) authentication);}catch (UsernameNotFoundException notFound) {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 {preAuthenticationChecks.check(user);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);}}

4.DaoAuthenticationProvider :用来获取UserDetails ,并对UserDetails做进一步的检查

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {@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 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"));}}protected void doAfterPropertiesSet() throws Exception {Assert.notNull(this.userDetailsService, "A UserDetailsService must be set");}protected final UserDetails retrieveUser(String username,UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {UserDetails loadedUser;try {loadedUser = this.getUserDetailsService().loadUserByUsername(username);}catch (UsernameNotFoundException notFound) {if (authentication.getCredentials() != null) {String presentedPassword = authentication.getCredentials().toString();passwordEncoder.isPasswordValid(userNotFoundEncodedPassword,presentedPassword, null);}throw notFound;}catch (Exception repositoryProblem) {throw new InternalAuthenticationServiceException(repositoryProblem.getMessage(), repositoryProblem);}if (loadedUser == null) {throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");}return loadedUser;}}

5.UserDetailsService:查询用户相关信息的核心接口

 * Copyright 2004, 2005, 2006 Acegi Technology Pty Limitedpackage org.springframework.security.core.userdetails;/** * Core interface which loads user-specific data. * <p> * It is used throughout the framework as a user DAO and is the strategy used by the * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider * DaoAuthenticationProvider}. * * <p> * The interface requires only one read-only method, which simplifies support for new * data-access strategies. * * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider * @see UserDetails * * @author Ben Alex */public interface UserDetailsService {// ~ Methods// ========================================================================================================/** * Locates the user based on the username. In the actual implementation, the search * may possibly be case sensitive, or case insensitive depending on how the * implementation instance is configured. In this case, the <code>UserDetails</code> * object that comes back may have a username that is of a different case than what * was actually requested.. * * @param username the username identifying the user whose data is required. * * @return a fully populated user record (never <code>null</code>) * * @throws UsernameNotFoundException if the user could not be found or the user has no * GrantedAuthority */UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;}



6.ResourceOwnerPasswordAccessTokenProvider:通过传人的用户名、密码获取访问令牌

public class ResourceOwnerPasswordAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {public boolean supportsResource(OAuth2ProtectedResourceDetails resource) {return resource instanceof ResourceOwnerPasswordResourceDetails && "password".equals(resource.getGrantType());}public boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) {return supportsResource(resource);}public OAuth2AccessToken refreshAccessToken(OAuth2ProtectedResourceDetails resource,OAuth2RefreshToken refreshToken, AccessTokenRequest request) throws UserRedirectRequiredException,OAuth2AccessDeniedException {MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();form.add("grant_type", "refresh_token");form.add("refresh_token", refreshToken.getValue());return retrieveToken(request, resource, form, new HttpHeaders());}public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {ResourceOwnerPasswordResourceDetails resource = (ResourceOwnerPasswordResourceDetails) details;return retrieveToken(request, resource, getParametersForTokenRequest(resource, request), new HttpHeaders());}}

到此,整个的认证流程已大致清楚。

那么我们要做什么,才能让认证流程按照我们的想法去做呢?

1.首先定义我们自己的UserDetails,该UserDetails 包含了用户的信息,和设备的信息。

public class SecurityUserDetails extends User {private static final long serialVersionUID = 7841964880444077547L;private final SecurityUser user;private final Collection<UUID> assignTos;private SecurityUserToken token;//设备信息,如imei(15位国际移动装备辨识码),iccid(20位集成电路卡识别码即SIM卡卡号)等信息public SecurityUserDetails(SecurityUser user, boolean accountNonExpired, boolean credentialsNonExpired,boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities, Collection<UUID> assignTos) {super(user.getUsername(), user.getEncodedPassword(), user.isEnabled() && (!user.isDeleted()), accountNonExpired,credentialsNonExpired, accountNonLocked, authorities);this.user = user;this.assignTos = assignTos;}public SecurityUserDetails(SecurityUser user, Collection<? extends GrantedAuthority> authorities,Collection<UUID> assignTos) {this(user, true, true, true, authorities, assignTos);}public SecurityUser getSecurityUser() {return user;}public SecurityUserToken getSecurityUserToken() {return token;}public void setSecurityUserToken(SecurityUserToken token) {this.token = token;}public Collection<UUID> getSecurityAssignTos() {return assignTos;}}

2.实现自己的UserDetailsService(此处只贴出接口,具体实现不赘述)

public interface SecurityUserTokenDetailsService extends UserDetailsService {public static final String KEY_AUTH_DETAILS = "SecurityUserToken";/** * load user by user token. *  * @param username *            user name Optional * @param iccid *            Optional Integrate circuit card identity. * @param imsi *            Optional International Mobile Subscriber Identification Number * @param imei *            Optional International Mobile Equipment Identity * @param meid *            Optional Mobile Equipment Identifier * @param udid *            Optional Unique Device Identifier * @param wifi *            Optional WIFI MAC Address *  * @return user name or null */SecurityUserToken loadUserByUserToken(String username, String iccid, String imsi, String imei, String meid,String udid, String wifi);}

3.SecurityAccessTokenProvider 用自己的tokenProvider 解析请求中的参数(设备信息)

public class SecurityAccessTokenProvider extends ResourceOwnerPasswordAccessTokenProvider {@Overridepublic boolean supportsResource(OAuth2ProtectedResourceDetails resource) {return resource instanceof SecurityResourceDetails && super.supportsResource(resource);}@Overridepublic boolean supportsRefresh(OAuth2ProtectedResourceDetails resource) {return supportsResource(resource);}@Overridepublic OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {SecurityResourceDetails resource = (SecurityResourceDetails) details;request.set("iccid", resource.getIccid());request.set("imsi", resource.getImsi());request.set("imei", resource.getImei());request.set("meid", resource.getMeid());request.set("udid", resource.getUdid());request.set("wifi", resource.getWifi());return super.obtainAccessToken(details, request);}}

4.SecurityAuthenticationProvider : 实现自己的AuthenticationProvider,“偷梁换柱”,当请求参数中不包含用户名和密码时,则去查找设备信息生成相应的认证对象。

我们在此像是加了一层拦截,又像是多了一种认证的选择。

public class SecurityAuthenticationProvider extends DaoAuthenticationProvider {@Autowiredpublic void setUserDetailsService(SecurityUserTokenDetailsService userDetailsService) {super.setUserDetailsService(userDetailsService);}@Autowiredpublic void setPasswordEncoder(PasswordEncoder passwordEncoder) {super.setPasswordEncoder(passwordEncoder);}@Overrideprotected void doAfterPropertiesSet() throws Exception {super.doAfterPropertiesSet();Assert.isInstanceOf(SecurityUserTokenDetailsService.class, getUserDetailsService(),"A securityUserTokenRepository must be set");}@Overrideprotected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {String presentedPassword = authentication.getCredentials().toString();if (!presentedPassword.isEmpty()) {super.additionalAuthenticationChecks(userDetails, authentication);}}@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {if (!(authentication instanceof UsernamePasswordAuthenticationToken)) {return super.authenticate(authentication);}authentication = preAuthenticate((UsernamePasswordAuthenticationToken) authentication);if (authentication.isAuthenticated()) {return authentication;}return super.authenticate(authentication);}@SuppressWarnings("unchecked")protected Authentication preAuthenticate(UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {String username = authentication.getName();String password = authentication.getCredentials() == null ? "" : authentication.getCredentials().toString();if ((!password.isEmpty()) && (!username.isEmpty())) {return authentication;}Object obj = authentication.getDetails();Map<Object, Object> details = null;if (obj instanceof Map) {details = (Map<Object, Object>) obj;}if (details == null) {return authentication;}String iccid = (obj = details.get("iccid")) == null ? "" : obj.toString();String imsi = (obj = details.get("imsi")) == null ? "" : obj.toString();String imei = (obj = details.get("imei")) == null ? "" : obj.toString();String meid = (obj = details.get("meid")) == null ? "" : obj.toString();String udid = (obj = details.get("udid")) == null ? "" : obj.toString();String wifi = (obj = details.get("wifi")) == null ? "" : obj.toString();SecurityUserTokenDetailsService userDetailsService = ((SecurityUserTokenDetailsService) getUserDetailsService());SecurityUserToken userToken = userDetailsService.loadUserByUserToken(username, iccid, imsi, imei, meid, udid,wifi);if (userToken == null) {return authentication;}username = userToken.getUser().getUsername();boolean cacheWasUsed = true;UserDetails user = getUserCache().getUserFromCache(username);if (user == null) {cacheWasUsed = false;try {user = retrieveUser(username, authentication);} catch (UsernameNotFoundException notFound) {logger.debug("User '" + username + "' not found");if (isHideUserNotFoundExceptions()) {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 {getPreAuthenticationChecks().check(user);additionalAuthenticationChecks(user, 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, authentication);getPreAuthenticationChecks().check(user);additionalAuthenticationChecks(user, authentication);} else {throw exception;}}getPostAuthenticationChecks().check(user);if (!cacheWasUsed) {getUserCache().putUserInCache(user);}Object principalToReturn = user;if (isForcePrincipalAsString()) {principalToReturn = user.getUsername();}if (user instanceof SecurityUserDetails) {((SecurityUserDetails) user).setSecurityUserToken(userToken);}details.put(SecurityUserTokenDetailsService.KEY_AUTH_DETAILS, userToken.getId());logger.debug("User '" + username + "' device login succeed!");return createSuccessAuthentication(principalToReturn, authentication, user);}}


总结:想要代码按照自己的流程去工作,首先要了解他原来的工作流程是怎样的。每次深入源码去了解每一个原理是一个很奇妙的过程,就像是一次探险之旅,这个过程会有些许艰难与困惑,但是当你洞穿了整个神秘的隧道也会觉得豁然开朗。拜读大师的杰作,受益匪浅。

原创粉丝点击