Browse Source

认证服务

Progr1mmer 6 years ago
parent
commit
d82b07f772
32 changed files with 3154 additions and 118 deletions
  1. 8 8
      common/common-entity/src/main/java/com/yihu/jw/entity/base/user/UserDO.java
  2. 9 6
      common/common-rest-model/src/main/java/com/yihu/jw/restmodel/base/user/UserVO.java
  3. 6 6
      server/svr-authentication/pom.xml
  4. 16 1
      server/svr-authentication/src/main/java/com/yihu/AuthServer.java
  5. 0 14
      server/svr-authentication/src/main/java/com/yihu/jw/oauth2/config/WlyyAuthorizationServerConfigurerAdapter.java
  6. 0 49
      server/svr-authentication/src/main/java/com/yihu/jw/oauth2/config/WlyyWebSecurityConfigurerAdapter.java
  7. 0 10
      server/svr-authentication/src/main/java/com/yihu/jw/oauth2/model/WlyyUserDetails.java
  8. 0 10
      server/svr-authentication/src/main/java/com/yihu/jw/oauth2/model/WlyyUserSimple.java
  9. 69 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/config/WlyyWebSecurityConfigurerAdapter.java
  10. 228 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/core/userdetails/SaltUser.java
  11. 17 12
      server/svr-authentication/src/main/java/com/yihu/jw/oauth2/core/WlyyUserDetailsService.java
  12. 52 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/model/Oauth2Envelop.java
  13. 220 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/model/WlyyUserDetails.java
  14. 165 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/model/WlyyUserSimple.java
  15. 65 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/common/WlyyOAuth2AccessToken.java
  16. 100 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/config/WlyyAuthorizationServerConfigurerAdapter.java
  17. 49 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/core/jdbc/WlyyJdbcUserSecurityService.java
  18. 44 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/core/redis/WlyyRedisVerifyCodeService.java
  19. 338 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/WlyyTokenGranter.java
  20. 60 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/client/WlyyJdbcClientRedirectUriService.java
  21. 699 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyAuthorizationEndpoint.java
  22. 336 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyLoginEndpoint.java
  23. 254 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyTokenEndpoint.java
  24. 148 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/error/WlyyOAuth2ExceptionTranslator.java
  25. 134 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/web/authentication/WlyyAuthenticationFailureHandler.java
  26. 18 0
      server/svr-authentication/src/main/java/com/yihu/jw/security/web/endpoint/BaseLoginEndpoint.java
  27. 38 0
      server/svr-authentication/src/main/resources/application.yml
  28. 29 0
      server/svr-authentication/src/main/resources/banner.txt
  29. 32 0
      server/svr-authentication/src/main/resources/bootstrap.yml
  30. 1 1
      svr/svr-base/src/main/java/com/yihu/jw/base/endpoint/user/UserEndpoint.java
  31. 19 0
      svr/svr-base/src/main/java/com/yihu/jw/base/service/UserService.java
  32. 0 1
      svr/svr-base/src/main/resources/application.yml

+ 8 - 8
common/common-entity/src/main/java/com/yihu/jw/entity/base/user/UserDO.java

@ -42,9 +42,9 @@ public class UserDO extends UuidIdentityEntityWithOperator {
    //邮箱
    //邮箱
    private String email;
    private String email;
    //是否可用
    //是否可用
    private Boolean isEnabled;
    private Boolean enabled;
    //是否锁定
    //是否锁定
    private Boolean isLocked;
    private Boolean locked;
    //锁定时间
    //锁定时间
    private Date lockedDate;
    private Date lockedDate;
    //最后登陆时间
    //最后登陆时间
@ -124,22 +124,22 @@ public class UserDO extends UuidIdentityEntityWithOperator {
        this.email = email;
        this.email = email;
    }
    }
    @Column(name = "is_enabled", nullable = false)
    @Column(name = "enabled", nullable = false)
    public Boolean getEnabled() {
    public Boolean getEnabled() {
        return isEnabled;
        return enabled;
    }
    }
    public void setEnabled(Boolean enabled) {
    public void setEnabled(Boolean enabled) {
        isEnabled = enabled;
        this.enabled = enabled;
    }
    }
    @Column(name = "is_locked", nullable = false)
    @Column(name = "locked", nullable = false)
    public Boolean getLocked() {
    public Boolean getLocked() {
        return isLocked;
        return locked;
    }
    }
    public void setLocked(Boolean locked) {
    public void setLocked(Boolean locked) {
        isLocked = locked;
        this.locked = locked;
    }
    }
    @Column(name = "locked_date")
    @Column(name = "locked_date")

+ 9 - 6
common/common-rest-model/src/main/java/com/yihu/jw/restmodel/base/user/UserVO.java

@ -1,5 +1,6 @@
package com.yihu.jw.restmodel.base.user;
package com.yihu.jw.restmodel.base.user;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.yihu.jw.entity.base.user.UserDO;
import com.yihu.jw.entity.base.user.UserDO;
import com.yihu.jw.restmodel.UuidIdentityVOWithOperator;
import com.yihu.jw.restmodel.UuidIdentityVOWithOperator;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModel;
@ -40,10 +41,10 @@ public class UserVO extends UuidIdentityVOWithOperator {
    private String email;
    private String email;
    //是否可用
    //是否可用
    @ApiModelProperty(value = "是否可用", example = "1")
    @ApiModelProperty(value = "是否可用", example = "1")
    private Boolean isEnabled;
    private Boolean enabled;
    //是否锁定
    //是否锁定
    @ApiModelProperty(value = "是否锁定", example = "1")
    @ApiModelProperty(value = "是否锁定", example = "1")
    private Boolean isLocked;
    private Boolean locked;
    //锁定时间
    //锁定时间
    @ApiModelProperty(value = "锁定时间", example = "2018-08-27 13:23:32")
    @ApiModelProperty(value = "锁定时间", example = "2018-08-27 13:23:32")
    private Date lockedDate;
    private Date lockedDate;
@ -119,21 +120,22 @@ public class UserVO extends UuidIdentityVOWithOperator {
    }
    }
    public Boolean getEnabled() {
    public Boolean getEnabled() {
        return isEnabled;
        return enabled;
    }
    }
    public void setEnabled(Boolean enabled) {
    public void setEnabled(Boolean enabled) {
        isEnabled = enabled;
        this.enabled = enabled;
    }
    }
    public Boolean getLocked() {
    public Boolean getLocked() {
        return isLocked;
        return locked;
    }
    }
    public void setLocked(Boolean locked) {
    public void setLocked(Boolean locked) {
        isLocked = locked;
        this.locked = locked;
    }
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
    public Date getLockedDate() {
    public Date getLockedDate() {
        return lockedDate;
        return lockedDate;
    }
    }
@ -142,6 +144,7 @@ public class UserVO extends UuidIdentityVOWithOperator {
        this.lockedDate = lockedDate;
        this.lockedDate = lockedDate;
    }
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
    public Date getLoginDate() {
    public Date getLoginDate() {
        return loginDate;
        return loginDate;
    }
    }

+ 6 - 6
server/svr-authentication/pom.xml

@ -55,22 +55,18 @@
        <dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <artifactId>spring-cloud-starter-config</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <artifactId>spring-cloud-starter-feign</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <artifactId>spring-cloud-starter-ribbon</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <groupId>org.springframework.boot</groupId>
@ -81,12 +77,10 @@
        <dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <artifactId>jedis</artifactId>
            <scope>${dependency.scope}</scope>
        </dependency>
        </dependency>
        <!-- Redis  -->
        <!-- Redis  -->
@ -106,6 +100,12 @@
        </dependency>
        </dependency>
        <!-- JdbcTemplate-->
        <!-- JdbcTemplate-->
        <dependency>
            <groupId>com.yihu</groupId>
            <artifactId>utils</artifactId>
            <version>2.0.0</version>
        </dependency>
    </dependencies>
    </dependencies>
</project>
</project>

+ 16 - 1
server/svr-authentication/src/main/java/com/yihu/AuthServer.java

@ -1,7 +1,22 @@
package com.yihu;
package com.yihu;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
/**
/**
 * Created by progr1mmer on 2018/8/29.
 * Created by progr1mmer on 2018/8/29.
 */
 */
public class AuthServer {
@SpringBootApplication
public class AuthServer extends SpringBootServletInitializer {
    public static void main(String[] args) {
        SpringApplication.run(AuthServer.class, args);
    }
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(AuthServer.class);
    }
}
}

+ 0 - 14
server/svr-authentication/src/main/java/com/yihu/jw/oauth2/config/WlyyAuthorizationServerConfigurerAdapter.java

@ -1,14 +0,0 @@
package com.yihu.jw.oauth2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
/**
 *
 * Created by progr1mmer on 2018/8/29.
 */
@Configuration
@EnableAuthorizationServer
public class WlyyAuthorizationServerConfigurerAdapter extends AuthorizationServerConfigurerAdapter {
}

+ 0 - 49
server/svr-authentication/src/main/java/com/yihu/jw/oauth2/config/WlyyWebSecurityConfigurerAdapter.java

@ -1,49 +0,0 @@
package com.yihu.jw.oauth2.config;
import com.yihu.jw.oauth2.core.WlyyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
 * Config - Spring Security
 * Created by progr1mmer on 2018/8/29.
 */
@Configuration
public class WlyyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    private AuthenticationProvider authenticationProvider;
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/**");
    }
    @Override
    protected void configure(AuthenticationManagerBuilder authBuilder) throws Exception {
        authBuilder.authenticationProvider(authenticationProvider);
    }
    @Bean
    WlyyUserDetailsService wlyyUserDetailsService(){
        return new WlyyUserDetailsService();
    }
    @Bean
    public AuthenticationProvider authenticationProvider(UserDetailsService userDetailsService) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(new Md5PasswordEncoder());
        return authenticationProvider;
    }
}

+ 0 - 10
server/svr-authentication/src/main/java/com/yihu/jw/oauth2/model/WlyyUserDetails.java

@ -1,10 +0,0 @@
package com.yihu.jw.oauth2.model;
import java.io.Serializable;
/**
 * Model - 认证用户详细信息
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyUserDetails implements Serializable {
}

+ 0 - 10
server/svr-authentication/src/main/java/com/yihu/jw/oauth2/model/WlyyUserSimple.java

@ -1,10 +0,0 @@
package com.yihu.jw.oauth2.model;
import java.io.Serializable;
/**
 * Model - 认证用户简略信息
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyUserSimple implements Serializable {
}

+ 69 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/config/WlyyWebSecurityConfigurerAdapter.java

@ -0,0 +1,69 @@
package com.yihu.jw.security.config;
import com.yihu.jw.security.web.authentication.WlyyAuthenticationFailureHandler;
import com.yihu.jw.security.core.userdetails.jdbc.WlyyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.authentication.dao.ReflectionSaltSource;
import org.springframework.security.authentication.encoding.Md5PasswordEncoder;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import javax.sql.DataSource;
/**
 * Config - Spring Security
 * Created by progr1mmer on 2018/8/29.
 */
@Configuration
@EnableWebSecurity
public class WlyyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/oauth/**");
    }
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/login/**").permitAll()
                .antMatchers("/**").hasRole("USER")
                .and().formLogin().loginPage("/login").failureHandler(new WlyyAuthenticationFailureHandler())
                .and().logout().logoutUrl("/logout").logoutSuccessUrl("/login")
                .and().headers().frameOptions().disable()
                .and().csrf().disable();
    }
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(authenticationProvider());
    }
    @Bean
    WlyyUserDetailsService wlyyUserDetailsService(){
        return new WlyyUserDetailsService(dataSource);
    }
    @Bean
    AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(wlyyUserDetailsService());
        authenticationProvider.setPasswordEncoder(new Md5PasswordEncoder());
        ReflectionSaltSource reflectionSaltSource = new ReflectionSaltSource();
        reflectionSaltSource.setUserPropertyToUse("salt");
        authenticationProvider.setSaltSource(reflectionSaltSource);
        authenticationProvider.setSaltSource(reflectionSaltSource);
        return authenticationProvider;
    }
}

+ 228 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/core/userdetails/SaltUser.java

@ -0,0 +1,228 @@
package com.yihu.jw.security.core.userdetails;
import org.springframework.security.core.CredentialsContainer;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;
import java.io.Serializable;
import java.util.*;
/**
 * Created by progr1mmer on 2018/8/29.
 */
public class SaltUser implements UserDetails, CredentialsContainer {
    private static final long serialVersionUID = 420L;
    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;
    private final String salt;
    public SaltUser(String username, String password, String salt, Collection<? extends GrantedAuthority> authorities) {
        this(username, password, salt, true, true, true, true, authorities);
    }
    public SaltUser(String username, String password, String salt, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        if(username != null && !"".equals(username) && password != null) {
            this.username = username;
            this.password = password;
            this.salt = salt;
            this.enabled = enabled;
            this.accountNonExpired = accountNonExpired;
            this.credentialsNonExpired = credentialsNonExpired;
            this.accountNonLocked = accountNonLocked;
            this.authorities = Collections.unmodifiableSet(sortAuthorities(authorities));
        } else {
            throw new IllegalArgumentException("Cannot pass null or empty values to constructor");
        }
    }
    public Collection<GrantedAuthority> getAuthorities() {
        return this.authorities;
    }
    public String getPassword() {
        return this.password;
    }
    public String getUsername() {
        return this.username;
    }
    public String getSalt() {
        return salt;
    }
    public boolean isEnabled() {
        return this.enabled;
    }
    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }
    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }
    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }
    public void eraseCredentials() {
        this.password = null;
    }
    private static SortedSet<GrantedAuthority> sortAuthorities(Collection<? extends GrantedAuthority> authorities) {
        Assert.notNull(authorities, "Cannot pass a null GrantedAuthority collection");
        SortedSet<GrantedAuthority> sortedAuthorities = new TreeSet(new SaltUser.AuthorityComparator());
        Iterator var2 = authorities.iterator();
        while(var2.hasNext()) {
            GrantedAuthority grantedAuthority = (GrantedAuthority)var2.next();
            Assert.notNull(grantedAuthority, "GrantedAuthority list cannot contain any null elements");
            sortedAuthorities.add(grantedAuthority);
        }
        return sortedAuthorities;
    }
    public boolean equals(Object rhs) {
        return rhs instanceof SaltUser?this.username.equals(((SaltUser)rhs).username):false;
    }
    public int hashCode() {
        return this.username.hashCode();
    }
    public 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(!this.authorities.isEmpty()) {
            sb.append("Granted Authorities: ");
            boolean first = true;
            Iterator var3 = this.authorities.iterator();
            while(var3.hasNext()) {
                GrantedAuthority auth = (GrantedAuthority)var3.next();
                if(!first) {
                    sb.append(",");
                }
                first = false;
                sb.append(auth);
            }
        } else {
            sb.append("Not granted any authorities");
        }
        return sb.toString();
    }
    public static SaltUser.UserBuilder withUsername(String username) {
        return (new SaltUser.UserBuilder()).username(username);
    }
    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;
        private UserBuilder() {
        }
        private SaltUser.UserBuilder username(String username) {
            Assert.notNull(username, "username cannot be null");
            this.username = username;
            return this;
        }
        public SaltUser.UserBuilder password(String password) {
            Assert.notNull(password, "password cannot be null");
            this.password = password;
            return this;
        }
        public SaltUser.UserBuilder roles(String... roles) {
            List<GrantedAuthority> authorities = new ArrayList(roles.length);
            String[] var3 = roles;
            int var4 = roles.length;
            for(int var5 = 0; var5 < var4; ++var5) {
                String role = var3[var5];
                Assert.isTrue(!role.startsWith("ROLE_"), role + " cannot start with ROLE_ (it is automatically added)");
                authorities.add(new SimpleGrantedAuthority("ROLE_" + role));
            }
            return this.authorities((List)authorities);
        }
        public SaltUser.UserBuilder authorities(GrantedAuthority... authorities) {
            return this.authorities(Arrays.asList(authorities));
        }
        public SaltUser.UserBuilder authorities(List<? extends GrantedAuthority> authorities) {
            this.authorities = new ArrayList(authorities);
            return this;
        }
        public SaltUser.UserBuilder authorities(String... authorities) {
            return this.authorities(AuthorityUtils.createAuthorityList(authorities));
        }
        public SaltUser.UserBuilder accountExpired(boolean accountExpired) {
            this.accountExpired = accountExpired;
            return this;
        }
        public SaltUser.UserBuilder accountLocked(boolean accountLocked) {
            this.accountLocked = accountLocked;
            return this;
        }
        public SaltUser.UserBuilder credentialsExpired(boolean credentialsExpired) {
            this.credentialsExpired = credentialsExpired;
            return this;
        }
        public SaltUser.UserBuilder disabled(boolean disabled) {
            this.disabled = disabled;
            return this;
        }
        public UserDetails build() {
            return new User(this.username, this.password, !this.disabled, !this.accountExpired, !this.credentialsExpired, !this.accountLocked, this.authorities);
        }
    }
    private static class AuthorityComparator implements Comparator<GrantedAuthority>, Serializable {
        private static final long serialVersionUID = 420L;
        private AuthorityComparator() {
        }
        public int compare(GrantedAuthority g1, GrantedAuthority g2) {
            return g2.getAuthority() == null?-1:(g1.getAuthority() == null?1:g1.getAuthority().compareTo(g2.getAuthority()));
        }
    }
}

+ 17 - 12
server/svr-authentication/src/main/java/com/yihu/jw/oauth2/core/WlyyUserDetailsService.java

@ -1,17 +1,20 @@
package com.yihu.jw.oauth2.core;
package com.yihu.jw.security.core.userdetails.jdbc;
import com.yihu.jw.oauth2.model.WlyyUserSimple;
import com.yihu.jw.security.core.userdetails.SaltUser;
import com.yihu.jw.security.model.WlyyUserDetails;
import com.yihu.jw.security.model.WlyyUserSimple;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.List;
/**
/**
@ -20,7 +23,11 @@ import java.util.List;
 */
 */
public class WlyyUserDetailsService extends JdbcDaoSupport implements UserDetailsService {
public class WlyyUserDetailsService extends JdbcDaoSupport implements UserDetailsService {
    private static final String DEFAULT_USER_DETAILS_STATEMENT = "SELECT * FROM user u WHERE u.username = ? OR u.mobile = ? OR u.idcard = ?";
    private static final String DEFAULT_USER_DETAILS_STATEMENT = "SELECT * FROM base_user u WHERE u.username = ? OR u.mobile = ? OR u.idcard = ?";
    public WlyyUserDetailsService(DataSource dataSource) {
        this.setDataSource(dataSource);
    }
    /**
    /**
     * 用户登录判读接口
     * 用户登录判读接口
@ -31,16 +38,11 @@ public class WlyyUserDetailsService extends JdbcDaoSupport implements UserDetail
     */
     */
    @Override
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<UserDetails> users = this.getJdbcTemplate().query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(UserDetails.class), username, username, username);
        List<WlyyUserDetails> users = this.getJdbcTemplate().query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(WlyyUserDetails.class), username, username, username);
        if (users == null || users.size() == 0) {
        if (users == null || users.size() == 0) {
            throw new UsernameNotFoundException(username);
            throw new UsernameNotFoundException(username);
        }
        }
        return new User(username, users.get(0).getPassword(), getGrantedAuthorities(username));
        /*if (username.equals("admin")){
            return new User("admin", "e10adc3949ba59abbe56e057f20f883e", getGrantedAuthorities(username));
        }
        return null;*/
        return new SaltUser(username, users.get(0).getPassword(), users.get(0).getSalt(), getGrantedAuthorities(username));
    }
    }
    private Collection<? extends GrantedAuthority> getGrantedAuthorities(String username) {
    private Collection<? extends GrantedAuthority> getGrantedAuthorities(String username) {
@ -49,7 +51,10 @@ public class WlyyUserDetailsService extends JdbcDaoSupport implements UserDetail
        return authorities;
        return authorities;
    }
    }
    public WlyyUserSimple loadUserSimpleByUsername(String username) throws UsernameNotFoundException {
    public WlyyUserSimple authSuccess (String username) throws UsernameNotFoundException {
        //更新登陆时间
        this.getJdbcTemplate().update("update base_user u set u.login_date = ? where u.username = ? or u.mobile = ? or u.idcard = ?", new Date(), username, username, username);
        //加载用户简略信息
        List<WlyyUserSimple> users = this.getJdbcTemplate().query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(WlyyUserSimple.class), username, username, username);
        List<WlyyUserSimple> users = this.getJdbcTemplate().query(DEFAULT_USER_DETAILS_STATEMENT, new BeanPropertyRowMapper(WlyyUserSimple.class), username, username, username);
        if (users == null || users.size() == 0) {
        if (users == null || users.size() == 0) {
            throw new UsernameNotFoundException(username);
            throw new UsernameNotFoundException(username);

+ 52 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/model/Oauth2Envelop.java

@ -0,0 +1,52 @@
package com.yihu.jw.security.model;
import com.fasterxml.jackson.annotation.JsonInclude;
import java.util.HashMap;
/**
 * Model - 认证失败信息
 * Created by progr1mmer on 2018/8/29.
 */
public class Oauth2Envelop<T> {
    protected String message;
    protected Integer status;
    private T obj = (T)new HashMap<>();
    public Oauth2Envelop(String message, Integer status) {
        this.message = message;
        this.status = status;
    }
    public Oauth2Envelop(String message, Integer status, T obj) {
        this.message = message;
        this.status = status;
        this.obj = obj;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    @JsonInclude(value = JsonInclude.Include.NON_EMPTY)
    public T getObj() {
        return obj;
    }
    public void setObj(T obj) {
        this.obj = obj;
    }
}

+ 220 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/model/WlyyUserDetails.java

@ -0,0 +1,220 @@
package com.yihu.jw.security.model;
import java.io.Serializable;
import java.util.Date;
/**
 * Model - 认证用户详细信息
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyUserDetails implements Serializable {
    /**
     * 性别
     */
    public enum Gender {
        //男
        male,
        //女
        female
    }
    private String id;
    //创建时间
    private Date createTime;
    //创建者
    private String createUser;
    //创建者
    private String createUserName;
    //更新时间
    private Date updateTime;
    //更新者
    private String updateUser;
    //更新者
    private String updateUserName;
    //用户名
    private String username;
    //密码
    private String password;
    //密码散列值
    private String salt;
    //姓名
    private String name;
    //性别
    private Gender gender;
    //身份证号码
    private String idcard;
    //手机号码
    private String mobile;
    //邮箱
    private String email;
    //是否可用
    private Boolean enabled;
    //是否锁定
    private Boolean locked;
    //锁定时间
    private Date lockedDate;
    //最后登陆时间
    private Date loginDate;
    //登陆失败次数
    private Integer loginFailureCount;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public String getCreateUser() {
        return createUser;
    }
    public void setCreateUser(String createUser) {
        this.createUser = createUser;
    }
    public String getCreateUserName() {
        return createUserName;
    }
    public void setCreateUserName(String createUserName) {
        this.createUserName = createUserName;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
    public String getUpdateUser() {
        return updateUser;
    }
    public void setUpdateUser(String updateUser) {
        this.updateUser = updateUser;
    }
    public String getUpdateUserName() {
        return updateUserName;
    }
    public void setUpdateUserName(String updateUserName) {
        this.updateUserName = updateUserName;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getSalt() {
        return salt;
    }
    public void setSalt(String salt) {
        this.salt = salt;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Gender getGender() {
        return gender;
    }
    public void setGender(Gender gender) {
        this.gender = gender;
    }
    public String getIdcard() {
        return idcard;
    }
    public void setIdcard(String idcard) {
        this.idcard = idcard;
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public Boolean isEnabled() {
        return enabled;
    }
    public void setEnabled(Boolean enabled) {
        this.enabled = enabled;
    }
    public Boolean isLocked() {
        return locked;
    }
    public void setLocked(Boolean locked) {
        this.locked = locked;
    }
    public Date getLockedDate() {
        return lockedDate;
    }
    public void setLockedDate(Date lockedDate) {
        this.lockedDate = lockedDate;
    }
    public Date getLoginDate() {
        return loginDate;
    }
    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
    public Integer getLoginFailureCount() {
        return loginFailureCount;
    }
    public void setLoginFailureCount(Integer loginFailureCount) {
        this.loginFailureCount = loginFailureCount;
    }
}

+ 165 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/model/WlyyUserSimple.java

@ -0,0 +1,165 @@
package com.yihu.jw.security.model;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.io.Serializable;
import java.util.Date;
/**
 * Model - 认证用户简略信息
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyUserSimple implements Serializable {
    /**
     * 性别
     */
    public enum Gender {
        //男
        male,
        //女
        female
    }
    private String id;
    //用户名
    private String username;
    //姓名
    private String name;
    //性别
    private Gender gender;
    //身份证号码
    private String idcard;
    //手机号码
    private String mobile;
    //邮箱
    private String email;
    //最后登陆时间
    private Date loginDate;
    //扩展字段
    private String user; //当前登陆凭证
    private String accessToken;
    private String refreshToken;
    private String tokenType;
    private int expiresIn;
    private String state;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Gender getGender() {
        return gender;
    }
    public void setGender(Gender gender) {
        this.gender = gender;
    }
    public String getIdcard() {
        return idcard;
    }
    public void setIdcard(String idcard) {
        this.idcard = idcard;
    }
    public String getMobile() {
        return mobile;
    }
    public void setMobile(String mobile) {
        this.mobile = mobile;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+08:00")
    public Date getLoginDate() {
        return loginDate;
    }
    public void setLoginDate(Date loginDate) {
        this.loginDate = loginDate;
    }
    public String getUser() {
        return user;
    }
    public void setUser(String user) {
        this.user = user;
    }
    //@JsonProperty(value = "access_token")
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    //@JsonProperty(value = "refresh_token")
    public String getRefreshToken() {
        return refreshToken;
    }
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
    //@JsonProperty(value = "token_type")
    public String getTokenType() {
        return tokenType;
    }
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }
    //@JsonProperty(value = "expires_in")
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}

+ 65 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/common/WlyyOAuth2AccessToken.java

@ -0,0 +1,65 @@
package com.yihu.jw.security.oauth2.common;
import java.io.Serializable;
/**
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyOAuth2AccessToken implements Serializable {
    private String accessToken;
    private String refreshToken;
    private String tokenType;
    private int expiresIn;
    private String scope;
    private String state;
    public String getAccessToken() {
        return accessToken;
    }
    public void setAccessToken(String accessToken) {
        this.accessToken = accessToken;
    }
    public String getRefreshToken() {
        return refreshToken;
    }
    public void setRefreshToken(String refreshToken) {
        this.refreshToken = refreshToken;
    }
    public String getTokenType() {
        return tokenType;
    }
    public void setTokenType(String tokenType) {
        this.tokenType = tokenType;
    }
    public int getExpiresIn() {
        return expiresIn;
    }
    public void setExpiresIn(int expiresIn) {
        this.expiresIn = expiresIn;
    }
    public String getScope() {
        return scope;
    }
    public void setScope(String scope) {
        this.scope = scope;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}

+ 100 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/config/WlyyAuthorizationServerConfigurerAdapter.java

@ -0,0 +1,100 @@
package com.yihu.jw.security.oauth2.config;
import com.yihu.jw.security.oauth2.provider.client.WlyyJdbcClientRedirectUriService;
import com.yihu.jw.security.oauth2.provider.WlyyTokenGranter;
import com.yihu.jw.security.oauth2.core.redis.WlyyRedisVerifyCodeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import javax.sql.DataSource;
/**
 * Config - Spring Oauth2
 * Created by progr1mmer on 2018/8/29.
 */
@Configuration
@EnableAuthorizationServer
public class WlyyAuthorizationServerConfigurerAdapter extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private DataSource dataSource;
    @Autowired
    private JedisConnectionFactory jedisConnectionFactory;
    @Autowired
    private JdbcTemplate jdbcTemplate;
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(redisTokenStore());
        endpoints.authorizationCodeServices(authorizationCodeServices());
    }
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(clientDetailsService());
    }
    /*@Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(new StandardPasswordEncoder());
    }*/
    @Bean
    @Primary
    WlyyTokenGranter wlyyTokenGranter (
            AuthenticationManager authenticationManager,
            AuthorizationServerTokenServices authorizationServerTokenServices) {
        WlyyTokenGranter tokenGranter = new WlyyTokenGranter(
                authenticationManager,
                authorizationServerTokenServices,
                authorizationCodeServices(),
                clientDetailsService(),
                new DefaultOAuth2RequestFactory(clientDetailsService()),
                wlyyRedisVerifyCodeService());
        return tokenGranter;
    }
    @Bean
    ClientDetailsService clientDetailsService() {
        return new JdbcClientDetailsService(dataSource);
    }
    @Bean
    RedisTokenStore redisTokenStore() {
        return new RedisTokenStore(jedisConnectionFactory);
    }
    @Bean
    WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService() {
        return new WlyyRedisVerifyCodeService(redisTemplate);
    }
    @Bean
    AuthorizationCodeServices authorizationCodeServices() {
        return new InMemoryAuthorizationCodeServices();
    }
    @Bean
    WlyyJdbcClientRedirectUriService wlyyJdbcClientRedirectUriService() {
        return new WlyyJdbcClientRedirectUriService(jdbcTemplate);
    }
}

+ 49 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/core/jdbc/WlyyJdbcUserSecurityService.java

@ -0,0 +1,49 @@
package com.yihu.jw.security.oauth2.core.jdbc;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.util.Assert;
import javax.sql.DataSource;
import java.util.List;
/**
 * Created by progr1mmer on 2018/1/8.
 */
public class WlyyJdbcUserSecurityService {
    private static final String DEFAULT_KEY_ID_SELECT_STATEMENT = "SELECT id FROM user_security WHERE public_key = ?";
    private static final String DEFAULT_USER_ID_BY_KEY_ID_SELECT_STATEMENT = "SELECT user_id FROM user_key WHERE key_id = ?";
    private static final String DEFAULT_USER_NAME_BY_USER_ID = "SELECT login_code FROM users WHERE id = ?";
    private JdbcTemplate jdbcTemplate;
    public WlyyJdbcUserSecurityService(DataSource dataSource) {
        Assert.notNull(dataSource, "DataSource required");
        this.jdbcTemplate = new JdbcTemplate(dataSource);
    }
    public String getDefaultKeyIdSelectStatement(String publicKey) {
        List<String> keyId = jdbcTemplate.queryForList(DEFAULT_KEY_ID_SELECT_STATEMENT, new String []{publicKey}, String.class);
        if(keyId.size() <= 0) {
            throw new InsufficientAuthenticationException("Illegal authorized pk.");
        }
        return keyId.get(0);
    }
    public String getDefaultUserIdByKeyIdSelectStatement(String keyId) {
        List<String> userId =  jdbcTemplate.queryForList(DEFAULT_USER_ID_BY_KEY_ID_SELECT_STATEMENT, new String []{keyId}, String.class);
        if(userId.size() <= 0) {
            throw new InsufficientAuthenticationException("Unauthorized security key. please check you pk.");
        }
        return userId.get(0);
    }
    public String getDefaultUserNameByUserId(String userId) {
        List<String> username = jdbcTemplate.queryForList(DEFAULT_USER_NAME_BY_USER_ID, new String []{userId}, String.class);
        if(username.size() <= 0) {
            throw new InsufficientAuthenticationException("Illegal user id. please check you pk.");
        }
        return username.get(0);
    }
}

+ 44 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/core/redis/WlyyRedisVerifyCodeService.java

@ -0,0 +1,44 @@
package com.yihu.jw.security.oauth2.core.redis;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import java.util.concurrent.TimeUnit;
/**
 * Created by progr1mmer on 2018/4/18.
 */
public class WlyyRedisVerifyCodeService {
    private final String KEY_SUFFIX = ":code";
    private final RedisTemplate redisTemplate;
    public WlyyRedisVerifyCodeService(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }
    public void store (String client_id, String username, String code, long expire) {
        String key = client_id + ":" + username + KEY_SUFFIX;
        redisTemplate.opsForValue().set(key, code);
        redisTemplate.expire(key, expire, TimeUnit.MILLISECONDS);
    }
    public Integer getExpireTime (String client_id, String username) {
        return new Long(redisTemplate.getExpire(client_id + ":" + username + KEY_SUFFIX)).intValue();
    }
    public boolean verification (String client_id, String username, String code) {
        if (StringUtils.isEmpty(code)) {
            return false;
        }
        String _code = (String) redisTemplate.opsForValue().get(client_id + ":" + username + KEY_SUFFIX);
        if (null == _code) {
            return false;
        }
        if (code.equalsIgnoreCase(_code)) {
            return true;
        }
        return false;
    }
}

+ 338 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/WlyyTokenGranter.java

@ -0,0 +1,338 @@
package com.yihu.jw.security.oauth2.provider;
import com.yihu.jw.security.oauth2.core.redis.WlyyRedisVerifyCodeService;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidClientException;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.security.oauth2.common.exceptions.RedirectMismatchException;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.implicit.ImplicitGrantService;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.token.AbstractTokenGranter;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.util.Assert;
import java.util.*;
/**
 * Token授权器。
 * @author Progr1mmer
 * @created 2018.01.09
 */
public class WlyyTokenGranter implements TokenGranter {
    private final Map<String, TokenGranter> tokenGranters = new HashMap<>();
    public WlyyTokenGranter(AuthenticationManager authenticationManager,
                            AuthorizationServerTokenServices tokenServices,
                            AuthorizationCodeServices authorizationCodeServices,
                            ClientDetailsService clientDetailsService,
                            OAuth2RequestFactory requestFactory,
                            WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService) {
        tokenGranters.put(EhrAuthorizationCodeGranter.GRANT_TYPE,
                new EhrAuthorizationCodeGranter(
                        tokenServices,
                        authorizationCodeServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrResourceOwnerPasswordTokenGranter.GRANT_TYPE,
                new EhrResourceOwnerPasswordTokenGranter(
                        authenticationManager,
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrRefreshTokenGranter.GRANT_TYPE,
                new EhrRefreshTokenGranter(
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrImplicitTokenGranter.GRANT_TYPE,
                new EhrImplicitTokenGranter(
                        tokenServices,
                        clientDetailsService,
                        requestFactory
                ));
        tokenGranters.put(EhrVerifyCodeTokenGranter.GRANT_TYPE,
                new EhrVerifyCodeTokenGranter(
                        authenticationManager,
                        tokenServices,
                        clientDetailsService,
                        requestFactory,
                        wlyyRedisVerifyCodeService
                ));
    }
    public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
        if (tokenGranters.get(grantType) != null) {
            OAuth2AccessToken grant = tokenGranters.get(grantType).grant(grantType, tokenRequest);
            return grant;
        }
        return null;
    }
    /**
     * authorization_code模式Token授权器。
     */
    public static class EhrAuthorizationCodeGranter extends AbstractTokenGranter {
        public static final String GRANT_TYPE = "authorization_code";
        private final AuthorizationCodeServices authorizationCodeServices;
        public EhrAuthorizationCodeGranter(AuthorizationServerTokenServices tokenServices,
                                           AuthorizationCodeServices authorizationCodeServices,
                                           ClientDetailsService clientDetailsService,
                                           OAuth2RequestFactory requestFactory) {
            this(tokenServices, authorizationCodeServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        protected EhrAuthorizationCodeGranter(AuthorizationServerTokenServices tokenServices,
                                              AuthorizationCodeServices authorizationCodeServices,
                                              ClientDetailsService clientDetailsService,
                                              OAuth2RequestFactory requestFactory,
                                              String grantType) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authorizationCodeServices = authorizationCodeServices;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = tokenRequest.getRequestParameters();
            String authorizationCode = parameters.get("code");
            String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI);
            if (authorizationCode == null) {
                throw new InvalidRequestException("An authorization code must be supplied.");
            }
            OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);
            if (storedAuth == null) {
                throw new InvalidGrantException("Invalid authorization code: " + authorizationCode);
            }
            OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request();
            String oauthUri;
            if (pendingOAuth2Request.getRedirectUri().indexOf("?") != -1) {
                oauthUri = pendingOAuth2Request.getRedirectUri().substring(0, pendingOAuth2Request.getRedirectUri().indexOf("?"));
            } else {
                oauthUri = pendingOAuth2Request.getRedirectUri();
            }
            // https://jira.springsource.org/browse/SECOAUTH-333
            // This might be null, if the authorization was done without the redirect_uri parameter
            String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get(
                    OAuth2Utils.REDIRECT_URI);
            if ((redirectUri != null || redirectUriApprovalParameter != null)
                    && !oauthUri.equals(redirectUri)) {
                throw new RedirectMismatchException("Redirect URI mismatch.");
            }
            String pendingClientId = pendingOAuth2Request.getClientId();
            String clientId = tokenRequest.getClientId();
            if (clientId != null && !clientId.equals(pendingClientId)) {
                // just a sanity check.
                throw new InvalidClientException("Client ID mismatch");
            }
            // Secret is not required in the authorization request, so it won't be available
            // in the pendingAuthorizationRequest. We do want to check that a secret is provided
            // in the token request, but that happens elsewhere.
            Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request
                    .getRequestParameters());
            // Combine the parameters adding the new ones last so they override if there are any clashes
            combinedParameters.putAll(parameters);
            // Make a new stored request with the combined parameters
            OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters);
            Authentication userAuth = storedAuth.getUserAuthentication();
            //再次通过pk获取当前请求的用户信息
            /*String pk = combinedParameters.get("pk");
            String keyId = ehrJDBCUserSecurityService.getDefaultKeyIdSelectStatement(pk);
            String userId = ehrJDBCUserSecurityService.getDefaultUserIdByKeyIdSelectStatement(keyId);
            String userName = ehrJDBCUserSecurityService.getDefaultUserNameByUserId(userId);
            UserDetails userDetails = ehrUserDetailsService.loadUserByUsername(userName);
            if (StringUtils.isEmpty(keyId) || StringUtils.isEmpty(userId) || StringUtils.isEmpty(userName)) {
                throw new InsufficientAuthenticationException("Illegal pk");
            }
            UsernamePasswordAuthenticationToken userToken = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());*/
            return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);
        }
    }
    /**
     * password模式Token授权器。
     */
    public static class EhrResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "password";
        private final AuthenticationManager authenticationManager;
        public EhrResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager,
                                                 AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
            this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        protected EhrResourceOwnerPasswordTokenGranter(AuthenticationManager authenticationManager, AuthorizationServerTokenServices tokenServices,
                                                    ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authenticationManager = authenticationManager;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
            String username = parameters.get("username");
            String password = parameters.get("password");
            // Protect from downstream leaks of password
            parameters.remove("password");
            Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);
            ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
            try {
                userAuth = authenticationManager.authenticate(userAuth);
            }
            catch (AccountStatusException ase) {
                //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31)
                throw new InvalidGrantException(ase.getMessage());
            }
            catch (BadCredentialsException e) {
                // If the username/password are wrong the spec says we should send 400/invalid grant
                throw new InvalidGrantException(e.getMessage());
            }
            if (userAuth == null || !userAuth.isAuthenticated()) {
                throw new InvalidGrantException("Could not authenticate user: " + username);
            }
            OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        }
    }
    /**
     * refresh模式Token授权器。
     */
    public static class EhrRefreshTokenGranter extends AbstractTokenGranter {
        static final String GRANT_TYPE = "refresh_token";
        public EhrRefreshTokenGranter(AuthorizationServerTokenServices tokenServices,
                                      ClientDetailsService clientDetailsService,
                                      OAuth2RequestFactory requestFactory) {
            super(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        @Override
        protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
            String refreshToken = tokenRequest.getRequestParameters().get(GRANT_TYPE);
            return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
        }
    }
    /**
     * Implicit模式Token授权器。
     */
    public static class EhrImplicitTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "implicit";
        public EhrImplicitTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory) {
            this(tokenServices, clientDetailsService, requestFactory, GRANT_TYPE);
        }
        protected EhrImplicitTokenGranter(AuthorizationServerTokenServices tokenServices, ClientDetailsService clientDetailsService,
                                       OAuth2RequestFactory requestFactory, String grantType) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest clientToken) {
            Authentication userAuth = SecurityContextHolder.getContext().getAuthentication();
            if (userAuth == null || !userAuth.isAuthenticated()) {
                throw new InsufficientAuthenticationException("There is no currently logged in user");
            }
            Assert.state(clientToken instanceof ImplicitTokenRequest, "An ImplicitTokenRequest is required here. Caller needs to wrap the TokenRequest.");
            OAuth2Request requestForStorage = ((ImplicitTokenRequest)clientToken).getOAuth2Request();
            return new OAuth2Authentication(requestForStorage, userAuth);
        }
        @SuppressWarnings("deprecation")
        public void setImplicitGrantService(ImplicitGrantService service) {
        }
    }
    /**
     * verify_code模式Token授权器。
     */
    public static class EhrVerifyCodeTokenGranter extends AbstractTokenGranter {
        private static final String GRANT_TYPE = "verify_code";
        private final AuthenticationManager authenticationManager;
        // Ehr Properties
        private final WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService;
        public EhrVerifyCodeTokenGranter(AuthenticationManager authenticationManager,
                                         AuthorizationServerTokenServices tokenServices,
                                         ClientDetailsService clientDetailsService,
                                         OAuth2RequestFactory requestFactory,
                                         WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService) {
            this(authenticationManager, tokenServices, clientDetailsService, requestFactory, GRANT_TYPE, wlyyRedisVerifyCodeService);
        }
        protected EhrVerifyCodeTokenGranter(AuthenticationManager authenticationManager,
                                            AuthorizationServerTokenServices tokenServices,
                                            ClientDetailsService clientDetailsService,
                                            OAuth2RequestFactory requestFactory,
                                            String grantType,
                                            WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService) {
            super(tokenServices, clientDetailsService, requestFactory, grantType);
            this.authenticationManager = authenticationManager;
            this.wlyyRedisVerifyCodeService = wlyyRedisVerifyCodeService;
        }
        @Override
        protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
            Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
            String client_id = parameters.get("client_id");
            String username = parameters.get("username");
            String verify_code = parameters.get("verify_code");
            if (!wlyyRedisVerifyCodeService.verification(client_id, username, verify_code)){
                throw new InvalidGrantException("Invalid verify_code");
            }
            Authentication userAuth = new UsernamePasswordAuthenticationToken(username, verify_code, getGrantedAuthorities(username));
            ((AbstractAuthenticationToken) userAuth).setDetails(parameters);
            OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
            return new OAuth2Authentication(storedOAuth2Request, userAuth);
        }
        private Collection<? extends GrantedAuthority> getGrantedAuthorities(String username) {
            Collection<GrantedAuthority> authorities = new ArrayList<>(1);
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            return authorities;
        }
    }
}

+ 60 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/client/WlyyJdbcClientRedirectUriService.java

@ -0,0 +1,60 @@
package com.yihu.jw.security.oauth2.provider.client;
import com.yihu.utils.network.IPInfoUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
 * Created by progr1mmer on 2017/9/19.
 */
public class WlyyJdbcClientRedirectUriService {
    private JdbcTemplate jdbcTemplate;
    public WlyyJdbcClientRedirectUriService(JdbcTemplate jdbcTemplate) {
        Assert.notNull(jdbcTemplate, "jdbcTemplate required");
        this.jdbcTemplate = jdbcTemplate;
    }
    public String loadValidUri (String clientId, String redirectUriParameter) {
        if (null == redirectUriParameter) {
            return redirectUriParameter;
        }
        if (redirectUriParameter.indexOf("?") != -1) {
            redirectUriParameter = redirectUriParameter.substring(0, redirectUriParameter.indexOf("?"));
        }
        Map<String, Object> redirects = loadRedirectUriByClientId(clientId);
        if (redirectUriParameter.equals(redirects.get("redirect_uri")) || redirectUriParameter.equals(redirects.get("redirect_out_uri"))) {
            return redirects.get("redirect_uri").toString();
        } else {
            return redirectUriParameter;
        }
    }
    public String loadAccessUri (String clientId, String redirectUriParameter) {
        if (redirectUriParameter != null) {
            return redirectUriParameter;
        }
        Map<String, Object> redirects = loadRedirectUriByClientId(clientId);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = IPInfoUtils.getIPAddress(request);
        boolean isInnerIp = IPInfoUtils.isInnerIP(ip);
        if (isInnerIp) {
            return (String) redirects.get("redirect_uri");
        } else {
            return redirects.get("redirect_out_uri") != null ? (String) redirects.get("redirect_out_uri") : (String) redirects.get("redirect_uri");
        }
    }
    private Map<String, Object> loadRedirectUriByClientId(String clientId) {
        String selectQuery = "select web_server_redirect_uri as redirect_uri, web_server_redirect_out_uri as redirect_out_uri from oauth_client_details where client_id = ?";
        Map<String, Object> resultMap = jdbcTemplate.queryForMap(selectQuery, clientId);
        return resultMap;
    }
}

+ 699 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyAuthorizationEndpoint.java

@ -0,0 +1,699 @@
package com.yihu.jw.security.oauth2.provider.endpoint;
import com.yihu.jw.security.core.userdetails.jdbc.WlyyUserDetailsService;
import com.yihu.jw.security.oauth2.provider.client.WlyyJdbcClientRedirectUriService;
import com.yihu.utils.network.IPInfoUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.approval.DefaultUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.code.InMemoryAuthorizationCodeServices;
import org.springframework.security.oauth2.provider.endpoint.*;
import org.springframework.security.oauth2.provider.implicit.ImplicitTokenRequest;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.support.DefaultSessionAttributeStore;
import org.springframework.web.bind.support.SessionAttributeStore;
import org.springframework.web.bind.support.SessionStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.net.URI;
import java.security.Principal;
import java.util.*;
/**
 * authorization_code & implicit & sso Endpoint
 *
 * <p>
 * Implementation of the Authorization Endpoint from the OAuth2 specification. Accepts authorization requests, and
 * handles user approval if the grant type is authorization code. The tokens themselves are obtained from the
 * {@link WlyyTokenEndpoint Token Endpoint}, except in the implicit grant type (where they come from the Authorization
 * Endpoint via <code>response_type=token</code>.
 * {@link WlyyLoginEndpoint Login Endpoint}, in order to verify the token to obtain user information.
 *
 * </p>
 *
 * <p>
 * This endpoint should be secured so that it is only accessible to fully authenticated users (as a minimum requirement)
 * since it represents a request from a valid user to act on his or her behalf.
 * </p>
 *
 * @author Progr1mmer
 * @created on 2018/8/29.
 */
@Controller
@SessionAttributes("authorizationRequest")
public class WlyyAuthorizationEndpoint extends AbstractEndpoint {
    private final Logger LOG = LoggerFactory.getLogger(getClass());
    private AuthorizationCodeServices authorizationCodeServices = new InMemoryAuthorizationCodeServices();
    private RedirectResolver redirectResolver = new DefaultRedirectResolver();
    private UserApprovalHandler userApprovalHandler = new DefaultUserApprovalHandler();
    private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
    private OAuth2RequestValidator oauth2RequestValidator = new DefaultOAuth2RequestValidator();
    private String userApprovalPage = "forward:/oauth/confirm_access";
    private String errorPage = "forward:/oauth/error";
    private Object implicitLock = new Object();
    private TokenStore tokenStore;
    @Autowired
    private WlyyUserDetailsService userDetailsService;
    @Autowired
    private AuthorizationServerEndpointsConfiguration authorizationServerEndpointsConfiguration;
    @Autowired
    private WlyyJdbcClientRedirectUriService wlyyJdbcClientRedirectUriService;
    @PostConstruct
    private void init() {
        AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer = authorizationServerEndpointsConfiguration.getEndpointsConfigurer();
        super.setTokenGranter(authorizationServerEndpointsConfigurer.getTokenGranter());
        super.setClientDetailsService(authorizationServerEndpointsConfigurer.getClientDetailsService());
        this.tokenStore = authorizationServerEndpointsConfigurer.getTokenStore();
        this.authorizationCodeServices = authorizationServerEndpointsConfigurer.getAuthorizationCodeServices();
    }
    public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
        this.sessionAttributeStore = sessionAttributeStore;
    }
    public void setErrorPage(String errorPage) {
        this.errorPage = errorPage;
    }
    @RequestMapping(value = "/oauth/authorize", method = RequestMethod.GET)
    public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
                                  SessionStatus sessionStatus, Principal principal) {
        // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
        // query off of the authorization request instead of referring back to the parameters map. The contents of the
        // parameters map will be stored without change in the AuthorizationRequest object once it is created.
        AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        /* default approve */
        authorizationRequest.setApproved(true);
        Set<String> responseTypes = authorizationRequest.getResponseTypes();
        if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        }
        if (authorizationRequest.getClientId() == null) {
            throw new InvalidClientException("A client id must be provided");
        }
        try {
            if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
                if (parameters.containsKey("ak")) {
                    String ak = parameters.get("ak");
                    UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken("admin", "cdb7df66a1955b0ed7402e665ed0586a", new ArrayList<>());
                    SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                    securityContext.setAuthentication(userAuth);
                    SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
                    securityContextHolderStrategy.setContext(securityContext);
                    principal = userAuth;
                } else {
                    throw new InsufficientAuthenticationException("ak must be provided.");
                }
            }
            /*if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
                throw new InsufficientAuthenticationException(
                        "User must be authenticated with Spring Security before authorization can be completed.");
            }*/
            ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
            // The resolved redirect URI is either the redirect_uri from the parameters or the one from
            // clientDetails. Either way we need to store it on the AuthorizationRequest.
            String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String validUrl = wlyyJdbcClientRedirectUriService.loadValidUri(authorizationRequest.getClientId(), redirectUriParameter);
            String resolvedRedirect = redirectResolver.resolveRedirect(validUrl, client);
            if (!StringUtils.hasText(resolvedRedirect)) {
                throw new RedirectMismatchException(
                        "A redirectUri must be either supplied or preconfigured in the ClientDetails");
            }
            // (customize) If redirectUriParameter is empty
            // load internal and external network ip information to redirect
            String accessUri = wlyyJdbcClientRedirectUriService.loadAccessUri(authorizationRequest.getClientId(), redirectUriParameter);
            authorizationRequest.setRedirectUri(accessUri);
            // We intentionally only validate the parameters requested by the client (ignoring any data that may have
            // been added to the request by the manager).
            oauth2RequestValidator.validateScope(authorizationRequest, client);
            // Some systems may allow for approval decisions to be remembered or approved by default. Check for
            // such logic here, and set the approved flag on the authorization request accordingly.
            authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
                    (Authentication) principal);
            // TODO: is this call necessary?
            boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
            authorizationRequest.setApproved(approved);
            // Validation is all done, so we can check for auto approval...
            if (authorizationRequest.isApproved()) {
                if (responseTypes.contains("token")) {
                    return getImplicitGrantResponse(authorizationRequest);
                }
                if (responseTypes.contains("code")) {
                    return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
                            (Authentication) principal));
                }
            }
            // Place auth request into the model so that it is stored in the session
            // for approveOrDeny to use. That way we make sure that auth request comes from the session,
            // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
            model.put("authorizationRequest", authorizationRequest);
            return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
        } catch (RuntimeException e) {
            sessionStatus.setComplete();
            throw e;
        }
    }
    @RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
    public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
                              SessionStatus sessionStatus, Principal principal) {
        if (!(principal instanceof Authentication)) {
            sessionStatus.setComplete();
            throw new InsufficientAuthenticationException(
                    "User must be authenticated with Spring Security before authorizing an access token.");
        }
        AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");
        if (authorizationRequest == null) {
            sessionStatus.setComplete();
            throw new InvalidRequestException("Cannot approve uninitialized authorization request.");
        }
        try {
            Set<String> responseTypes = authorizationRequest.getResponseTypes();
            authorizationRequest.setApprovalParameters(approvalParameters);
            authorizationRequest = userApprovalHandler.updateAfterApproval(authorizationRequest,
                    (Authentication) principal);
            boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
            authorizationRequest.setApproved(approved);
            if (authorizationRequest.getRedirectUri() == null) {
                sessionStatus.setComplete();
                throw new InvalidRequestException("Cannot approve request when no redirect URI is provided.");
            }
            if (!authorizationRequest.isApproved()) {
                return new RedirectView(getUnsuccessfulRedirect(authorizationRequest,
                        new UserDeniedAuthorizationException("User denied access"), responseTypes.contains("token")),
                        false, true, false);
            }
            if (responseTypes.contains("token")) {
                return getImplicitGrantResponse(authorizationRequest).getView();
            }
            return getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal);
        }
        finally {
            sessionStatus.setComplete();
        }
    }
    @RequestMapping(value = "/oauth/sso", method = RequestMethod.GET)
    public ModelAndView sso(Map<String, Object> model, @RequestParam Map<String, String> parameters,
                                  SessionStatus sessionStatus, Principal principal) {
        // Pull out the authorization request first, using the OAuth2RequestFactory. All further logic should
        // query off of the authorization request instead of referring back to the parameters map. The contents of the
        // parameters map will be stored without change in the AuthorizationRequest object once it is created.
        AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        /* default approve */
        authorizationRequest.setApproved(true);
        Set<String> responseTypes = authorizationRequest.getResponseTypes();
        /* Does not accept code type verification */
        if (!responseTypes.contains("token")) {
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        }
        /*if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
            throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
        }*/
        if (authorizationRequest.getClientId() == null) {
            throw new InvalidClientException("A client id must be provided");
        }
        try {
            /*
             * load verification information by token
             */
            if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
                if (parameters.containsKey("access_token")) {
                    String token = parameters.get("access_token");
                    OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
                    if (null == oAuth2AccessToken) {
                        throw new InvalidTokenException("Invalid access_token");
                    }
                    if (oAuth2AccessToken.isExpired()) {
                        throw new InvalidTokenException("Expired accessToken");
                    }
                    OAuth2Authentication authentication = tokenStore.readAuthentication(token);
                    if (authentication != null) {
                        String userName = authentication.getName();
                        UserDetails userDetails = userDetailsService.loadUserByUsername(userName);
                        UsernamePasswordAuthenticationToken userAuth = new UsernamePasswordAuthenticationToken(userDetails.getUsername(), userDetails.getPassword(), userDetails.getAuthorities());
                        SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                        securityContext.setAuthentication(userAuth);
                        SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();
                        securityContextHolderStrategy.setContext(securityContext);
                        principal = userAuth;
                    } else {
                        throw new InvalidTokenException("Cant not load authentication");
                    }
                } else {
                    throw new InvalidRequestException("access_token must be provided.");
                }
            }
            ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());
            // The resolved redirect URI is either the redirect_uri from the parameters or the one from
            // clientDetails. Either way we need to store it on the AuthorizationRequest.
            String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String validUrl = wlyyJdbcClientRedirectUriService.loadValidUri(authorizationRequest.getClientId(), redirectUriParameter);
            String resolvedRedirect = redirectResolver.resolveRedirect(validUrl, client);
            if (!StringUtils.hasText(resolvedRedirect)) {
                throw new RedirectMismatchException(
                        "A redirectUri must be either supplied or preconfigured in the ClientDetails");
            }
            // (customize) If redirectUriParameter is empty
            // load internal and external network ip information to redirect
            String accessUri = wlyyJdbcClientRedirectUriService.loadAccessUri(authorizationRequest.getClientId(), redirectUriParameter);
            authorizationRequest.setRedirectUri(accessUri);
            // We intentionally only validate the parameters requested by the client (ignoring any data that may have
            // been added to the request by the manager).
            oauth2RequestValidator.validateScope(authorizationRequest, client);
            // Some systems may allow for approval decisions to be remembered or approved by default. Check for
            // such logic here, and set the approved flag on the authorization request accordingly.
            authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
                    (Authentication) principal);
            // TODO: is this call necessary?
            boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
            authorizationRequest.setApproved(approved);
            // Validation is all done, so we can check for auto approval...
            if (authorizationRequest.isApproved()) {
                if (responseTypes.contains("token")) {
                    return getImplicitGrantResponse(authorizationRequest);
                }
                if (responseTypes.contains("code")) {
                    return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
                            (Authentication) principal));
                }
            }
            // Place auth request into the model so that it is stored in the session
            // for approveOrDeny to use. That way we make sure that auth request comes from the session,
            // so any auth request parameters passed to approveOrDeny will be ignored and retrieved from the session.
            model.put("authorizationRequest", authorizationRequest);
            return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
        } catch (RuntimeException e) {
            sessionStatus.setComplete();
            throw e;
        }
    }
    // We need explicit approval from the user.
    private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
                                                     AuthorizationRequest authorizationRequest, Authentication principal) {
        LOG.debug("Loading user approval page: " + userApprovalPage);
        model.putAll(userApprovalHandler.getUserApprovalRequest(authorizationRequest, principal));
        return new ModelAndView(userApprovalPage, model);
    }
    // We can grant a token and return it with implicit approval.
    private ModelAndView getImplicitGrantResponse(AuthorizationRequest authorizationRequest) {
        try {
            TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(authorizationRequest, "implicit");
            OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
            OAuth2AccessToken accessToken = getAccessTokenForImplicitGrant(tokenRequest, storedOAuth2Request);
            if (accessToken == null) {
                throw new UnsupportedResponseTypeException("Unsupported response type: token");
            }
            return new ModelAndView(new RedirectView(appendAccessToken(authorizationRequest, accessToken), false, true,
                    false));
        }
        catch (OAuth2Exception e) {
            return new ModelAndView(new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, true), false,
                    true, false));
        }
    }
    private OAuth2AccessToken getAccessTokenForImplicitGrant(TokenRequest tokenRequest,
                                                             OAuth2Request storedOAuth2Request) {
        OAuth2AccessToken accessToken = null;
        // These 1 method calls have to be atomic, otherwise the ImplicitGrantService can have a race condition where
        // one thread removes the token request before another has a chance to redeem it.
        synchronized (this.implicitLock) {
            accessToken = getTokenGranter().grant("implicit",
                    new ImplicitTokenRequest(tokenRequest, storedOAuth2Request));
        }
        return accessToken;
    }
    private View getAuthorizationCodeResponse(AuthorizationRequest authorizationRequest, Authentication authUser) {
        try {
            return new RedirectView(getSuccessfulRedirect(authorizationRequest,
                    generateCode(authorizationRequest, authUser)), false, true, false);
        }
        catch (OAuth2Exception e) {
            return new RedirectView(getUnsuccessfulRedirect(authorizationRequest, e, false), false, true, false);
        }
    }
    private String appendAccessToken(AuthorizationRequest authorizationRequest, OAuth2AccessToken accessToken) {
        Map<String, Object> vars = new LinkedHashMap<String, Object>();
        Map<String, String> keys = new HashMap<String, String>();
        if (accessToken == null) {
            throw new InvalidRequestException("An implicit grant could not be made");
        }
        vars.put("accessToken", accessToken.getValue());
        vars.put("tokenType", accessToken.getTokenType());
        String state = authorizationRequest.getState();
        if (state != null) {
            vars.put("state", state);
        }
        Date expiration = accessToken.getExpiration();
        if (expiration != null) {
            long expires_in = (expiration.getTime() - System.currentTimeMillis()) / 1000;
            vars.put("expiresIn", expires_in);
        }
        String originalScope = authorizationRequest.getRequestParameters().get(OAuth2Utils.SCOPE);
        if (originalScope == null || !OAuth2Utils.parseParameterList(originalScope).equals(accessToken.getScope())) {
            vars.put("scope", OAuth2Utils.formatParameterList(accessToken.getScope()));
        }
        Map<String, Object> additionalInformation = accessToken.getAdditionalInformation();
        for (String key : additionalInformation.keySet()) {
            Object value = additionalInformation.get(key);
            if (value != null) {
                keys.put("extra_" + key, key);
                vars.put("extra_" + key, value);
            }
        }
        // Do not include the refresh token (even if there is one)
        return append(authorizationRequest.getRedirectUri(), vars, keys, true);
    }
    private String generateCode(AuthorizationRequest authorizationRequest, Authentication authentication)
            throws AuthenticationException {
        try {
            OAuth2Request storedOAuth2Request = getOAuth2RequestFactory().createOAuth2Request(authorizationRequest);
            OAuth2Authentication combinedAuth = new OAuth2Authentication(storedOAuth2Request, authentication);
            String code = authorizationCodeServices.createAuthorizationCode(combinedAuth);
            return code;
        }
        catch (OAuth2Exception e) {
            if (authorizationRequest.getState() != null) {
                e.addAdditionalInformation("state", authorizationRequest.getState());
            }
            throw e;
        }
    }
    private String getSuccessfulRedirect(AuthorizationRequest authorizationRequest, String authorizationCode) {
        if (authorizationCode == null) {
            throw new IllegalStateException("No authorization code found in the current request scope.");
        }
        Map<String, String> query = new LinkedHashMap<String, String>();
        query.put("code", authorizationCode);
        String state = authorizationRequest.getState();
        if (state != null) {
            query.put("state", state);
        }
        return append(authorizationRequest.getRedirectUri(), query, false);
    }
    private String getUnsuccessfulRedirect(AuthorizationRequest authorizationRequest, OAuth2Exception failure,
                                           boolean fragment) {
        if (authorizationRequest == null || authorizationRequest.getRedirectUri() == null) {
            // we have no redirect for the user. very sad.
            throw new UnapprovedClientAuthenticationException("Authorization failure, and no redirect URI.", failure);
        }
        Map<String, String> query = new LinkedHashMap<String, String>();
        query.put("error", failure.getOAuth2ErrorCode());
        query.put("error_description", failure.getMessage());
        if (authorizationRequest.getState() != null) {
            query.put("state", authorizationRequest.getState());
        }
        if (failure.getAdditionalInformation() != null) {
            for (Map.Entry<String, String> additionalInfo : failure.getAdditionalInformation().entrySet()) {
                query.put(additionalInfo.getKey(), additionalInfo.getValue());
            }
        }
        return append(authorizationRequest.getRedirectUri(), query, fragment);
    }
    private String append(String base, Map<String, ?> query, boolean fragment) {
        return append(base, query, null, fragment);
    }
    private String append(String base, Map<String, ?> query, Map<String, String> keys, boolean fragment) {
        UriComponentsBuilder template = UriComponentsBuilder.newInstance();
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(base);
        URI redirectUri;
        try {
            // assume it's encoded to start with (if it came in over the wire)
            redirectUri = builder.build(true).toUri();
        }
        catch (Exception e) {
            // ... but allow client registrations to contain hard-coded non-encoded values
            redirectUri = builder.build().toUri();
            builder = UriComponentsBuilder.fromUri(redirectUri);
        }
        template.scheme(redirectUri.getScheme()).port(redirectUri.getPort()).host(redirectUri.getHost())
                .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath());
        if (fragment) {
            StringBuilder values = new StringBuilder();
            if (redirectUri.getFragment() != null) {
                String append = redirectUri.getFragment();
                values.append(append);
            }
            for (String key : query.keySet()) {
                if (values.length() > 0) {
                    values.append("&");
                }
                String name = key;
                if (keys != null && keys.containsKey(key)) {
                    name = keys.get(key);
                }
                values.append(name + "={" + key + "}");
            }
            if (values.length() > 0) {
                template.fragment(values.toString());
            }
            UriComponents encoded = template.build().expand(query).encode();
            builder.fragment(encoded.getFragment());
        }
        else {
            for (String key : query.keySet()) {
                String name = key;
                if (keys != null && keys.containsKey(key)) {
                    name = keys.get(key);
                }
                template.queryParam(name, "{" + key + "}");
            }
            template.fragment(redirectUri.getFragment());
            UriComponents encoded = template.build().expand(query).encode();
            builder.query(encoded.getQuery());
        }
        return builder.build().toUriString();
    }
    public void setUserApprovalPage(String userApprovalPage) {
        this.userApprovalPage = userApprovalPage;
    }
    public void setAuthorizationCodeServices(AuthorizationCodeServices authorizationCodeServices) {
        this.authorizationCodeServices = authorizationCodeServices;
    }
    public void setRedirectResolver(RedirectResolver redirectResolver) {
        this.redirectResolver = redirectResolver;
    }
    public void setUserApprovalHandler(UserApprovalHandler userApprovalHandler) {
        this.userApprovalHandler = userApprovalHandler;
    }
    public void setOAuth2RequestValidator(OAuth2RequestValidator oauth2RequestValidator) {
        this.oauth2RequestValidator = oauth2RequestValidator;
    }
    @SuppressWarnings("deprecation")
    public void setImplicitGrantService(
            org.springframework.security.oauth2.provider.implicit.ImplicitGrantService implicitGrantService) {
    }
    @ExceptionHandler(Exception.class)
    public ModelAndView handleGlobalException(Exception e) throws Exception {
        LOG.info(e.getMessage(), e);
        return new ModelAndView(errorPage, Collections.singletonMap("error", new OAuth2Exception(e.getMessage(), e)));
    }
    /*@ExceptionHandler(ClientRegistrationException.class)
    public ModelAndView handleClientRegistrationException(Exception e, ServletWebRequest webRequest) throws Exception {
        LOG.info("Handling ClientRegistrationException error: " + e.getMessage());
        return handleException(new BadClientCredentialsException(), webRequest);
    }
    @ExceptionHandler(OAuth2Exception.class)
    public ModelAndView handleOAuth2Exception(OAuth2Exception e, ServletWebRequest webRequest) throws Exception {
        LOG.info("Handling OAuth2 error: " + e.getSummary());
        return handleException(e, webRequest);
    }
    @ExceptionHandler(HttpSessionRequiredException.class)
    public ModelAndView handleHttpSessionRequiredException(HttpSessionRequiredException e, ServletWebRequest webRequest)
            throws Exception {
        LOG.info("Handling Session required error: " + e.getMessage());
        return handleException(new AccessDeniedException("Could not obtain authorization request from session", e),
                webRequest);
    }*/
    private ModelAndView handleException(Exception e, ServletWebRequest webRequest) throws Exception {
        ResponseEntity<OAuth2Exception> translate = getExceptionTranslator().translate(e);
        webRequest.getResponse().setStatus(translate.getStatusCode().value());
        if (e instanceof ClientAuthenticationException || e instanceof RedirectMismatchException) {
            return new ModelAndView(errorPage, Collections.singletonMap("error", translate.getBody()));
        }
        AuthorizationRequest authorizationRequest = null;
        try {
            authorizationRequest = getAuthorizationRequestForError(webRequest);
            String requestedRedirectParam = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI);
            String requestedRedirect = redirectResolver.resolveRedirect(requestedRedirectParam,
                    getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId()));
            authorizationRequest.setRedirectUri(requestedRedirect);
            String redirect = getUnsuccessfulRedirect(authorizationRequest, translate.getBody(), authorizationRequest
                    .getResponseTypes().contains("token"));
            return new ModelAndView(new RedirectView(redirect, false, true, false));
        }
        catch (OAuth2Exception ex) {
            // If an AuthorizationRequest cannot be created from the incoming parameters it must be
            // an error. OAuth2Exception can be handled this way. Other exceptions will generate a standard 500
            // response.
            return new ModelAndView(errorPage, Collections.singletonMap("error", translate.getBody()));
        }
    }
    private AuthorizationRequest getAuthorizationRequestForError(ServletWebRequest webRequest) {
        // If it's already there then we are in the approveOrDeny phase and we can use the saved request
        AuthorizationRequest authorizationRequest = (AuthorizationRequest) sessionAttributeStore.retrieveAttribute(
                webRequest, "authorizationRequest");
        if (authorizationRequest != null) {
            return authorizationRequest;
        }
        Map<String, String> parameters = new HashMap<String, String>();
        Map<String, String[]> map = webRequest.getParameterMap();
        for (String key : map.keySet()) {
            String[] values = map.get(key);
            if (values != null && values.length > 0) {
                parameters.put(key, values[0]);
            }
        }
        try {
            return getOAuth2RequestFactory().createAuthorizationRequest(parameters);
        }
        catch (Exception e) {
            return getDefaultOAuth2RequestFactory().createAuthorizationRequest(parameters);
        }
    }
}

+ 336 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyLoginEndpoint.java

@ -0,0 +1,336 @@
package com.yihu.jw.security.oauth2.provider.endpoint;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yihu.jw.security.oauth2.core.redis.WlyyRedisVerifyCodeService;
import com.yihu.jw.security.oauth2.provider.error.WlyyOAuth2ExceptionTranslator;
import com.yihu.jw.security.oauth2.provider.WlyyTokenGranter;
import com.yihu.jw.security.core.userdetails.jdbc.WlyyUserDetailsService;
import com.yihu.jw.security.model.Oauth2Envelop;
import com.yihu.jw.security.model.WlyyUserSimple;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
/**
 * <p>
 * Endpoint for default login verify and sso login token verify
 * will return the user's basic information and token certificate
 * </p>
 *
 * @author Progr1mmer
 * @created on 2018/8/29.
 */
@RestController
public class WlyyLoginEndpoint extends AbstractEndpoint {
    private final Logger LOG = LoggerFactory.getLogger(getClass());
    private OAuth2RequestFactory oAuth2RequestFactory;
    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private WlyyTokenGranter tokenGranter;
    @Autowired
    private WlyyOAuth2ExceptionTranslator wlyyOAuth2ExceptionTranslator;
    @Autowired
    private RedisTokenStore tokenStore;
    @Autowired
    private WlyyUserDetailsService userDetailsService;
    @Autowired
    private WlyyRedisVerifyCodeService wlyyRedisVerifyCodeService;
    @Autowired
    private ObjectMapper objectMapper;
    @PostConstruct
    private void init() {
        super.setTokenGranter(tokenGranter);
    }
    @RequestMapping(value = "/oauth/login", method = RequestMethod.POST)
    public ResponseEntity<Oauth2Envelop<WlyyUserSimple>> login(@RequestParam Map<String, String> parameters) {
        String client_id = parameters.get("client_id");
        if (StringUtils.isEmpty(client_id)) {
            throw new InvalidRequestException("client_id");
        }
        if (StringUtils.isEmpty(parameters.get("verify_code"))) {
            parameters.put("grant_type", "password");
        } else {
            parameters.put("grant_type", "verify_code");
        }
        ClientDetails authenticatedClient = clientDetailsService.loadClientByClientId(client_id);
        TokenRequest tokenRequest = oAuth2RequestFactory.createTokenRequest(parameters, authenticatedClient);
        if (authenticatedClient != null) {
            oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
        }
        OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
        if (token == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
        }
        /*如果是移动端登陆则移除之前的token,
        在网关处通过HTTP状态码告知前端是过期(402)还是账号在别处登陆(403),
        实现同一账号只能在一处登陆*/
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        if (request.getHeader("login-device") != null && request.getHeader("login-device").equals("mobile")) {
            tokenStore.removeAccessToken(token.getValue());
            tokenStore.removeRefreshToken(token.getRefreshToken().getValue());
            token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
        }
        if (token == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
        }
        WlyyUserSimple wlyyUserSimple = userDetailsService.authSuccess(parameters.get("username"));
        wlyyUserSimple.setAccessToken(token.getValue());
        wlyyUserSimple.setTokenType(token.getTokenType());
        wlyyUserSimple.setExpiresIn(token.getExpiresIn());
        wlyyUserSimple.setRefreshToken(token.getRefreshToken().getValue());
        wlyyUserSimple.setUser(parameters.get("username"));
        wlyyUserSimple.setState(parameters.get("state"));
        return getResponse(wlyyUserSimple);
    }
    @RequestMapping(value = "/oauth/sso", method = RequestMethod.POST)
    public ResponseEntity<Oauth2Envelop<WlyyUserSimple>> sso(@RequestParam Map<String, String> parameters) {
        String clientId = parameters.get("client_id");
        String accessToken = parameters.get("access_token");
        if (StringUtils.isEmpty(clientId)) {
            throw new InvalidRequestException("client_id");
        }
        if (StringUtils.isEmpty(accessToken)) {
            throw new InvalidRequestException("access_token");
        }
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(accessToken);
        if (null == oAuth2AccessToken) {
            throw new InvalidTokenException("Invalid access_token");
        }
        if (oAuth2AccessToken.isExpired()) {
            throw new InvalidTokenException("Expired accessToken");
        }
        OAuth2Authentication authentication = tokenStore.readAuthentication(accessToken);
        if (null == authentication) {
            throw new InvalidTokenException("Cant not load authentication");
        }
        String _clientId = authentication.getOAuth2Request().getClientId();
        if (!clientId.equals(_clientId)) {
            throw new InvalidTokenException("Provided token does not belong to the request client");
        }
        WlyyUserSimple wlyyUserSimple = userDetailsService.authSuccess(authentication.getName());
        wlyyUserSimple.setAccessToken(oAuth2AccessToken.getValue());
        wlyyUserSimple.setTokenType(oAuth2AccessToken.getTokenType());
        wlyyUserSimple.setExpiresIn(oAuth2AccessToken.getExpiresIn());
        wlyyUserSimple.setRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
        wlyyUserSimple.setUser(authentication.getName());
        wlyyUserSimple.setState(parameters.get("state"));
        return getResponse(wlyyUserSimple);
    }
    @RequestMapping(value = "/oauth/logout", method = RequestMethod.POST)
    public ResponseEntity<Oauth2Envelop> logout(@RequestParam Map<String, String> parameters, HttpServletRequest request) {
        String token = request.getHeader("token");
        if (null == token) {
            token = parameters.get("token");
        }
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(token);
        if (oAuth2AccessToken != null) {
            tokenStore.removeAccessToken(oAuth2AccessToken.getValue());
            tokenStore.removeRefreshToken(oAuth2AccessToken.getRefreshToken().getValue());
        }
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        Oauth2Envelop oauth2Envelop = new Oauth2Envelop("logout", 200);
        return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
    }
    /*@RequestMapping(value = ServiceApi.Authentication.VerifyCode, method = RequestMethod.POST)
    public ResponseEntity<Envelop> verifyCode(@RequestParam Map<String, String> parameters) throws  Exception{
        Envelop envelop = new Envelop();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        if (StringUtils.isEmpty(username)){
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("手机号码【"+username+"】不能为空!");
            return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
        }
        VerifyCode verifyCode = new VerifyCode();
        //手机短信验证码
        RandomUtil randomUtil = new RandomUtil();
        String random = randomUtil.getRandomString(6);
        //发送短信
        String api = "MsgGW.Sms.send";
        String content = "尊敬的用户:欢迎使用健康上饶,您的验证码为:【" + random + "】,有效期10分钟,请尽快完成注册。若非本人操作,请忽略。";
        Map<String, String> apiParamMap = new HashMap<>();
        //手机号码
        apiParamMap.put("mobile", username);
        //业务标签
        apiParamMap.put("handlerId", fzHandlerId);
        //短信内容
        apiParamMap.put("content", content);
        //渠道号
        apiParamMap.put("clientId", fzClientId);
        String result = null;
        Envelop resultEnvelop = fzApiClient.fzInnerApi(api, objectMapper.writeValueAsString(apiParamMap), 1);
        if (resultEnvelop.isSuccessFlg()) {
            result = resultEnvelop.getObj().toString();
        }
        if (!StringUtils.isEmpty(result)) {
            Map<String, Object> resultMap = objectMapper.readValue(result, Map.class);
            Integer resultCode = 0;
            if (null != resultMap.get("Code") && !"".equals(resultMap.get("Code"))) {
                resultCode = Integer.valueOf(resultMap.get("Code").toString());
            }
            if (resultCode == 10000) {
                verifyCode.setExpiresIn(600);
                verifyCode.setNextRequestTime(60);
                //验证码有效期
                ehrRedisVerifyCodeService.store(client_id, username, random, 600000);
                envelop.setSuccessFlg(true);
                envelop.setObj(verifyCode);
            } else if(resultCode == -201){
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信已达每天限制的次数(10次)!");
            } else if(resultCode == -200){
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信发送频率太快(不能低于60s)!");
            } else {
                envelop.setSuccessFlg(false);
                envelop.setErrorCode(resultCode);
                envelop.setErrorMsg("短信验证码发送失败!");
            }
        } else {
            envelop.setSuccessFlg(false);
            envelop.setErrorCode(ErrorCode.REQUEST_NOT_COMPLETED.value());
            envelop.setErrorMsg("短信验证码发送失败!");
        }
        return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
    }
    @RequestMapping(value = ServiceApi.Authentication.VerifyCodeExpire, method = RequestMethod.POST)
    public ResponseEntity<VerifyCode> verifyCodeExpire(@RequestParam Map<String, String> parameters) {
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        VerifyCode verifyCode = new VerifyCode();
        int expiresIn = ehrRedisVerifyCodeService.getExpireTime(client_id, username);
        int nextRequestTime = 60 + (expiresIn - 600 ) > 0 ? 60 + (expiresIn - 600 ) : 0;
        verifyCode.setNextRequestTime(nextRequestTime);
        verifyCode.setExpiresIn(expiresIn);
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>(verifyCode, headers, HttpStatus.OK);
    }
    @RequestMapping(value = ServiceApi.Authentication.VerifyCodeValidate, method = RequestMethod.POST)
    public ResponseEntity<Envelop> verifyCodeValidate(@RequestParam Map<String, String> parameters) throws  Exception{
        Envelop envelop = new Envelop();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        String client_id = parameters.get("client_id");
        String username = parameters.get("username");
        String verifyCode = parameters.get("verify_code");
        if (StringUtils.isEmpty(verifyCode)){
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("验证码不能为空!");
            return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
        }
        boolean _verify = ehrRedisVerifyCodeService.verification(client_id, username, verifyCode);
        if (_verify){
            envelop.setSuccessFlg(true);
        } else {
            envelop.setSuccessFlg(false);
            envelop.setErrorMsg("请输入正确的验证码!");
        }
        return new ResponseEntity<>(envelop, headers, HttpStatus.OK);
    }*/
    @Override
    protected TokenGranter getTokenGranter() {
        return this.tokenGranter;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.state(clientDetailsService != null, "ClientDetailsService must be provided");
        Assert.state(authenticationManager != null, "AuthenticationManager must be provided");
        oAuth2RequestFactory = new DefaultOAuth2RequestFactory(clientDetailsService);
    }
    private ResponseEntity<Oauth2Envelop<WlyyUserSimple>> getResponse(WlyyUserSimple ehrUserSimple) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        Oauth2Envelop<WlyyUserSimple> oauth2Envelop = new Oauth2Envelop<>("login", 200, ehrUserSimple);
        return new ResponseEntity<>(oauth2Envelop, headers, HttpStatus.OK);
    }
    @Override
    protected WebResponseExceptionTranslator getExceptionTranslator() {
        return wlyyOAuth2ExceptionTranslator;
    }
   @ExceptionHandler(Exception.class)
    public ResponseEntity<Oauth2Envelop> handleException(Exception e) throws Exception {
        LOG.info(e.getMessage(), e);
        if (e instanceof UsernameNotFoundException) {
            return handleOAuth2Exception(new Oauth2Envelop("用户未注册!", HttpStatus.UNAUTHORIZED.value()));
        } else if (e instanceof NoSuchClientException) {
            return handleOAuth2Exception(new Oauth2Envelop("应用未注册!", HttpStatus.UNAUTHORIZED.value()));
        } else if (e instanceof InvalidGrantException) {
            if (e.getMessage().equals("verify_code")) {
                return handleOAuth2Exception(new Oauth2Envelop("验证码有误!", HttpStatus.UNAUTHORIZED.value()));
            }
            return handleOAuth2Exception(new Oauth2Envelop("密码有误!", HttpStatus.UNAUTHORIZED.value()));
        } else if (e instanceof InvalidTokenException) {
            return handleOAuth2Exception(new Oauth2Envelop("Token有误!", HttpStatus.UNAUTHORIZED.value()));
        } else if (e instanceof InvalidRequestException) {
            return handleOAuth2Exception(new Oauth2Envelop("参数" + e.getMessage() + "缺失!", HttpStatus.UNAUTHORIZED.value()));
        }
        return handleOAuth2Exception(new Oauth2Envelop(e.getMessage(), -1));
    }
    private ResponseEntity<Oauth2Envelop> handleOAuth2Exception(Oauth2Envelop authenticationFailed) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, authenticationFailed.getMessage()));
        ResponseEntity<Oauth2Envelop> response = new ResponseEntity<>(authenticationFailed, headers, HttpStatus.OK);
        return response;
    }
}

+ 254 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/endpoint/WlyyTokenEndpoint.java

@ -0,0 +1,254 @@
/*
 * Copyright (c) 2015 MONKEYK Information Technology Co. Ltd
 * www.monkeyk.com
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * MONKEYK Information Technology Co. Ltd ("Confidential Information").
 * You shall not disclose such Confidential Information and shall use
 * it only in accordance with the terms of the license agreement you
 * entered into with MONKEYK Information Technology Co. Ltd.
 */
package com.yihu.jw.security.oauth2.provider.endpoint;
import com.yihu.jw.security.model.Oauth2Envelop;
import com.yihu.jw.security.oauth2.common.WlyyOAuth2AccessToken;
import com.yihu.jw.security.oauth2.provider.WlyyTokenGranter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.*;
import org.springframework.security.oauth2.common.util.OAuth2Utils;
import org.springframework.security.oauth2.provider.*;
import org.springframework.security.oauth2.provider.endpoint.AbstractEndpoint;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator;
import org.springframework.util.StringUtils;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.Principal;
import java.util.*;
/**
 * <p>
 * Endpoint for token requests as described in the OAuth2 spec. Clients post requests with a <code>grant_type</code>
 * parameter (e.g. "authorization_code") and other parameters as determined by the grant type. Supported grant types are
 * handled by the provided {@link com.yihu.jw.security.oauth2.provider.WlyyTokenGranter token
 * granter}.
 * </p>
 *
 * <p>
 * Clients must be authenticated using a Spring Security {@link Authentication} to access this endpoint, and the client
 * id is extracted from the authentication token. The best way to arrange this (as per the OAuth2 spec) is to use HTTP
 * basic authentication for this endpoint with standard Spring Security support.
 * </p>
 *
 * @author Progr1mmer
 * @created on 2018/8/29.
 */
@RestController
public class WlyyTokenEndpoint extends AbstractEndpoint {
    private final Logger LOG = LoggerFactory.getLogger(getClass());
    @Autowired
    private WlyyTokenGranter tokenGranter;
    @Autowired
    private ClientDetailsService clientDetailsService;
    private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator();
    private Set<HttpMethod> allowedRequestMethods = new HashSet<>(Arrays.asList(HttpMethod.POST));
    @PostConstruct
    private void init() {
        super.setTokenGranter(tokenGranter);
        super.setClientDetailsService(clientDetailsService);
    }
    @RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
    public ResponseEntity<WlyyOAuth2AccessToken> getAccessToken(@RequestParam
            Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        if (!allowedRequestMethods.contains(HttpMethod.GET)) {
            throw new HttpRequestMethodNotSupportedException("GET");
        }
        return postAccessToken(parameters);
    }
    @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
    public ResponseEntity<WlyyOAuth2AccessToken> postAccessToken(@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
        String clientId = getClientId(parameters);
        if (StringUtils.isEmpty(clientId)) {
            throw new InvalidClientException("Missing clientId");
        }
        ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);
        TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);
        /*
         * customize
         * skip this step
         */
        /*if (clientId != null && !clientId.equals("")) {
            // Only validate the client details if a client authenticated during this
            // request.
            if (!clientId.equals(tokenRequest.getClientId())) {
                // double check to make sure that the client ID in the token request is the same as that in the
                // authenticated client
                throw new InvalidClientException("Given client ID does not match authenticated client");
            }
        }*/
        if (authenticatedClient != null) {
            oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
        }
        if (!StringUtils.hasText(tokenRequest.getGrantType())) {
            throw new InvalidRequestException("Missing grant type");
        }
        if (tokenRequest.getGrantType().equals("implicit")) {
            throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
        }
        if (isAuthCodeRequest(parameters)) {
            // The scope was requested or determined during the authorization step
            if (!tokenRequest.getScope().isEmpty()) {
                logger.debug("Clearing scope of incoming token request");
                tokenRequest.setScope(Collections.<String> emptySet());
            }
        }
        if (isRefreshTokenRequest(parameters)) {
            // A refresh token has its own default scopes, so we should ignore any added by the factory here.
            tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
        }
        OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
        if (token == null) {
            throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
        }
        // customize ---------------------------------
        WlyyOAuth2AccessToken wlyyOAuth2AccessToken = new WlyyOAuth2AccessToken();
        wlyyOAuth2AccessToken.setAccessToken(token.getValue());
        wlyyOAuth2AccessToken.setTokenType(token.getTokenType());
        wlyyOAuth2AccessToken.setExpiresIn(token.getExpiresIn());
        wlyyOAuth2AccessToken.setRefreshToken(token.getRefreshToken().getValue());
        wlyyOAuth2AccessToken.setScope(org.apache.commons.lang.StringUtils.join(token.getScope(), " "));
        wlyyOAuth2AccessToken.setState(parameters.get("state"));
        // customize ---------------------------------
        return getResponse(wlyyOAuth2AccessToken);
    }
    /**
     * @param principal the currently authentication principal
     * @return a client id if there is one in the principal
     */
    @Deprecated
    protected String getClientId(Principal principal) {
        Authentication client = (Authentication) principal;
        if (!client.isAuthenticated()) {
            throw new InsufficientAuthenticationException("The client is not authenticated.");
        }
        String clientId = client.getName();
        if (client instanceof OAuth2Authentication) {
            // Might be a client and user combined authentication
            clientId = ((OAuth2Authentication) client).getOAuth2Request().getClientId();
        }
        return clientId;
    }
    /**
     * @param parameters the request parameters (omit the basic certification)
     * @return a client id if there is one in the principal
     */
    protected String getClientId(Map<String, String> parameters) {
        return parameters.get("client_id");
    }
    /*@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<OAuth2Exception> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
    @ExceptionHandler(Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }
    @ExceptionHandler(ClientRegistrationException.class)
    public ResponseEntity<OAuth2Exception> handleClientRegistrationException(Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(new BadClientCredentialsException());
    }
    @ExceptionHandler(OAuth2Exception.class)
    public ResponseEntity<OAuth2Exception> handleException(OAuth2Exception e) throws Exception {
        logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage());
        return getExceptionTranslator().translate(e);
    }*/
    /**
     * (customize)
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Oauth2Envelop> handleException(Exception e) throws Exception {
        LOG.info(e.getMessage(), e);
        return handleOAuth2Exception(new Oauth2Envelop(e.getMessage(), -1));
    }
    private ResponseEntity<WlyyOAuth2AccessToken> getResponse(WlyyOAuth2AccessToken accessToken) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        return new ResponseEntity<>(accessToken, headers, HttpStatus.OK);
    }
    /**
     * customize
     * return results directly
     * @param authenticationFailed
     * @return
     * @throws IOException
     */
    private ResponseEntity<Oauth2Envelop> handleOAuth2Exception(Oauth2Envelop authenticationFailed) throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, authenticationFailed.getMessage()));
        ResponseEntity<Oauth2Envelop> response = new ResponseEntity<>(authenticationFailed, headers, HttpStatus.OK);
        return response;
    }
    private boolean isRefreshTokenRequest(Map<String, String> parameters) {
        return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null;
    }
    private boolean isAuthCodeRequest(Map<String, String> parameters) {
        return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null;
    }
    public void setOAuth2RequestValidator(OAuth2RequestValidator oAuth2RequestValidator) {
        this.oAuth2RequestValidator = oAuth2RequestValidator;
    }
    public void setAllowedRequestMethods(Set<HttpMethod> allowedRequestMethods) {
        this.allowedRequestMethods = allowedRequestMethods;
    }
}

+ 148 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/oauth2/provider/error/WlyyOAuth2ExceptionTranslator.java

@ -0,0 +1,148 @@
package com.yihu.jw.security.oauth2.provider.error;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InsufficientScopeException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.stereotype.Component;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import java.io.IOException;
/**
 * OAuth2异常转换
 */
@Component
public class WlyyOAuth2ExceptionTranslator implements WebResponseExceptionTranslator {
    private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer();
    public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
        // Try to extract a SpringSecurityException from the stacktrace
        Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e);
        Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(
                OAuth2Exception.class, causeChain);
        if (ase != null) {
            return handleOAuth2Exception((OAuth2Exception) ase);
        }
        ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class,
                causeChain);
        if (ase != null) {
            return handleOAuth2Exception(new UnauthorizedException(e.getMessage(), e));
        }
        ase = (AccessDeniedException) throwableAnalyzer
                .getFirstThrowableOfType(AccessDeniedException.class, causeChain);
        if (ase instanceof AccessDeniedException) {
            return handleOAuth2Exception(new ForbiddenException(ase.getMessage(), ase));
        }
        ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer
                .getFirstThrowableOfType(HttpRequestMethodNotSupportedException.class, causeChain);
        if (ase instanceof HttpRequestMethodNotSupportedException) {
            return handleOAuth2Exception(new MethodNotAllowed(ase.getMessage(), ase));
        }
        return handleOAuth2Exception(new ServerErrorException(e.getMessage(), e));
    }
    private ResponseEntity<OAuth2Exception> handleOAuth2Exception(OAuth2Exception e) throws IOException {
        int status = e.getHttpErrorCode();
        HttpHeaders headers = new HttpHeaders();
        headers.set("Cache-Control", "no-store");
        headers.set("Pragma", "no-cache");
        if (status == HttpStatus.UNAUTHORIZED.value() || (e instanceof InsufficientScopeException)) {
            headers.set("WWW-Authenticate", String.format("%s %s", OAuth2AccessToken.BEARER_TYPE, e.getSummary()));
        }
        ResponseEntity<OAuth2Exception> response = new ResponseEntity<>(e, headers,
                HttpStatus.valueOf(status));
        return response;
    }
    public void setThrowableAnalyzer(ThrowableAnalyzer throwableAnalyzer) {
        this.throwableAnalyzer = throwableAnalyzer;
    }
    @SuppressWarnings("serial")
    private static class ForbiddenException extends OAuth2Exception {
        public ForbiddenException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "access_denied";
        }
        public int getHttpErrorCode() {
            return 403;
        }
    }
    @SuppressWarnings("serial")
    private static class ServerErrorException extends OAuth2Exception {
        public ServerErrorException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "server_error";
        }
        public int getHttpErrorCode() {
            return 500;
        }
    }
    @SuppressWarnings("serial")
    private static class UnauthorizedException extends OAuth2Exception {
        public UnauthorizedException(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "unauthorized";
        }
        public int getHttpErrorCode() {
            return 401;
        }
    }
    @SuppressWarnings("serial")
    private static class MethodNotAllowed extends OAuth2Exception {
        public MethodNotAllowed(String msg, Throwable t) {
            super(msg, t);
        }
        public String getOAuth2ErrorCode() {
            return "method_not_allowed";
        }
        public int getHttpErrorCode() {
            return 405;
        }
    }
}

+ 134 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/web/authentication/WlyyAuthenticationFailureHandler.java

@ -0,0 +1,134 @@
package com.yihu.jw.security.web.authentication;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
 * Created by progr1mmer on 2018/8/29.
 */
public class WlyyAuthenticationFailureHandler 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();
    public WlyyAuthenticationFailureHandler() {
    }
    public WlyyAuthenticationFailureHandler(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 {
        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) {
        Assert.isTrue(UrlUtils.isValidRedirectUrl(defaultFailureUrl), "'"
                + defaultFailureUrl + "' is not a valid redirect URL");
        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;
    }
}

+ 18 - 0
server/svr-authentication/src/main/java/com/yihu/jw/security/web/endpoint/BaseLoginEndpoint.java

@ -0,0 +1,18 @@
package com.yihu.jw.security.web.endpoint;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * Endpoint - 基础登陆
 * Created by progr1mmer on 2018/8/29.
 */
@RestController
public class BaseLoginEndpoint {
    @GetMapping(value = "/login")
    public String login() {
        return "Get method no support for path /login";
    }
}

+ 38 - 0
server/svr-authentication/src/main/resources/application.yml

@ -0,0 +1,38 @@
server:
  port: 10260
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    max-active: 20
    max-idle: 8
    min-idle: 8
    validation-query: SELECT 1
    test-on-borrow: true
  redis:
    database: 0 # Database index used by the connection factory.
    timeout: 0 # Connection timeout in milliseconds.
    pool:
      max-active: 8 # Max number of connections that can be allocated by the pool at a given time. Use a negative value for no limit.
      max-idle: 8 # Max number of "idle" connections in the pool. Use a negative value to indicate an unlimited number of idle connections.
      max-wait: -1 # Maximum amount of time (in milliseconds) a connection allocation should block before throwing an exception when the pool is exhausted. Use a negative value to block indefinitely.
      min-idle: 1 # Target for the minimum number of idle connections to maintain in the pool. This setting only has an effect if it is positive.
logging:
  level:
    org:
      springframework:
        security: DEBUG
---
spring:
  profiles: jwdev
  datasource:
    url: jdbc:mysql://172.19.103.77/base?useUnicode:true&characterEncoding=utf-8&autoReconnect=true
    username: root
    password: 123456
  redis:
    host: 172.19.103.47
    port: 6379
    password: redis!@456

+ 29 - 0
server/svr-authentication/src/main/resources/banner.txt

@ -0,0 +1,29 @@
                                   _oo8oo_
                                  o8888888o
                                  88" . "88
                                  (| -_- |)
                                  0\  =  /0
                                ___/'==='\___
                              .' \\|     |// '.
                             / \\|||  :  |||// \
                            / _||||| -:- |||||_ \
                           |   | \\\  -  /// |   |
                           | \_|  ''\---/''  |_/ |
                           \  .-\__  '-'  __/-.  /
                         ___'. .'  /--.--\  '. .'___
                      ."" '<  '.___\_<|>_/___.'  >' "".
                     | | :  `- \`.:`\ _ /`:.`/ -`  : | |
                     \  \ `-.   \_ __\ /__ _/   .-` /  /
                 =====`-.____`.___ \_____/ ___.`____.-`=====
                                   `=---=`
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                      佛祖保佑     永不宕机     永无Bug
 __   _      ___        __    _    _____  _     ____  _     _____  _   __     __   _____  _   ___   _
( (` \ \  / | |_)      / /\  | | |  | |  | |_| | |_  | |\ |  | |  | | / /`   / /\   | |  | | / / \ | |\ |
_)_)  \_\/  |_| \     /_/--\ \_\_/  |_|  |_| | |_|__ |_| \|  |_|  |_| \_\_, /_/--\  |_|  |_| \_\_/ |_| \|

+ 32 - 0
server/svr-authentication/src/main/resources/bootstrap.yml

@ -0,0 +1,32 @@
spring:
  application:
    name: svr-authentication
  cloud:
    config:
      failFast: true
      username: jw
      password: jkzl
---
spring:
  profiles: jwdev
  cloud:
    config:
      uri: ${wlyy.spring.config.uri:http://172.17.110.212:1221}
      label: ${wlyy.spring.config.label:jwdev}
---
spring:
  profiles: jwtest
  cloud:
    config:
      uri: ${wlyy.pring.config.uri:http://172.17.110.212:1221}
      label: ${wlyy.spring.config.label:jwdev}
---
spring:
  profiles: jwprod
  cloud:
    config:
      uri: ${wlyy.pring.config.uri:http://172.17.110.212:1221}
      label: ${wlyy.spring.config.label:jwprod}

+ 1 - 1
svr/svr-base/src/main/java/com/yihu/jw/base/endpoint/user/UserEndpoint.java

@ -36,7 +36,7 @@ public class UserEndpoint extends EnvelopRestEndpoint {
            @ApiParam(name = "json_data", value = "Json数据", required = true)
            @ApiParam(name = "json_data", value = "Json数据", required = true)
            @RequestBody String jsonData) throws Exception {
            @RequestBody String jsonData) throws Exception {
        UserDO userDO = toEntity(jsonData, UserDO.class);
        UserDO userDO = toEntity(jsonData, UserDO.class);
        userDO = userService.save(userDO);
        userDO = userService.register(userDO);
        return success(userDO, UserVO.class);
        return success(userDO, UserVO.class);
    }
    }

+ 19 - 0
svr/svr-base/src/main/java/com/yihu/jw/base/service/UserService.java

@ -3,7 +3,10 @@ package com.yihu.jw.base.service;
import com.yihu.jw.base.dao.UserDao;
import com.yihu.jw.base.dao.UserDao;
import com.yihu.jw.entity.base.user.UserDO;
import com.yihu.jw.entity.base.user.UserDO;
import com.yihu.mysql.query.BaseJpaService;
import com.yihu.mysql.query.BaseJpaService;
import com.yihu.utils.security.MD5;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
/**
/**
 * Service - 后台管理员
 * Service - 后台管理员
@ -11,4 +14,20 @@ import org.springframework.stereotype.Service;
 */
 */
@Service
@Service
public class UserService extends BaseJpaService<UserDO, UserDao> {
public class UserService extends BaseJpaService<UserDO, UserDao> {
    @Autowired
    private UserDao userDao;
    public UserDO register(UserDO userDO) {
        userDO.setSalt(randomString(5));
        userDO.setEnabled(true);
        userDO.setLocked(false);
        userDO.setLoginFailureCount(0);
        String password = userDO.getPassword();
        if (StringUtils.isEmpty(password)) {
            password = userDO.getIdcard().substring(0, 5);
        }
        userDO.setPassword(MD5.md5Hex(password + "{" + userDO.getSalt() + "}"));
        return userDao.save(userDO);
    }
}
}

+ 0 - 1
svr/svr-base/src/main/resources/application.yml

@ -1,4 +1,3 @@
##如果有配置服务的话,远程服务器和本地服务器配置不一致的情况下,优先远程的为主
server:
server:
  #port: ${server.svr-base-port}
  #port: ${server.svr-base-port}
  port: 10020
  port: 10020