├── README.md ├── authorization-server ├── pom.xml └── src │ ├── main │ ├── java │ │ └── cn │ │ │ └── merryyou │ │ │ └── security │ │ │ ├── SpringBoot2Oauth2Application.java │ │ │ ├── config │ │ │ └── TokenStoreConfig.java │ │ │ ├── handler │ │ │ └── AppLoginInSuccessHandler.java │ │ │ ├── properties │ │ │ ├── OAuth2ClientProperties.java │ │ │ ├── OAuth2CoreConfig.java │ │ │ └── OAuth2Properties.java │ │ │ ├── security │ │ │ ├── MyUserDetailsService.java │ │ │ ├── SecurityConfig.java │ │ │ └── jwt │ │ │ │ └── MerryyouJwtTokenEnhancer.java │ │ │ ├── server │ │ │ ├── MerryyouAuthorizationServerConfig.java │ │ │ └── MerryyouResourceServerConfig.java │ │ │ └── utils │ │ │ └── JsonUtil.java │ └── resources │ │ └── application.yml │ └── test │ └── java │ └── cn │ └── merryyou │ └── security │ └── SpringBoot2Oauth2Test.java ├── pom.xml └── resource-server ├── pom.xml ├── resource-server.iml └── src └── main ├── java └── cn │ └── merryyou │ └── security │ ├── SpringBoot2Oauth2ResourceApplication.java │ └── resource │ └── MerryyouResourceServerConfiguration.java └── resources └── application.yml /README.md: -------------------------------------------------------------------------------- 1 | # springboot2.0-oauth2 2 | [![GitHub stars](https://img.shields.io/github/stars/longfeizheng/springboot2.0-oauth2.svg?style=flat&label=Star)](https://github.com/longfeizheng/springboot2.0-oauth2/stargazers) 3 | [![GitHub forks](https://img.shields.io/github/forks/longfeizheng/springboot2.0-oauth2.svg?style=flat&label=Fork)](https://github.com/longfeizheng/springboot2.0-oauth2/fork) 4 | [![GitHub watchers](https://img.shields.io/github/watchers/longfeizheng/springboot2.0-oauth2.svg?style=flat&label=Watch)](https://github.com/longfeizheng/springboot2.0-oauth2/watchers) 5 | - [springboot 2.0 整合Oauth2](https://longfeizheng.github.io/2018/04/29/Spring-Boot-2.0-%E6%95%B4%E5%90%88-Spring-Security-Oauth2/) 6 | - [SpringBott 1.5.6.RELEASE整合Oauth2](https://github.com/longfeizheng/security-oauth2) 7 | 8 | 9 | #### 授权码模式 10 | 11 | [![https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth202.gif](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth202.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth202.gif")](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth202.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth202.gif") 12 | 13 | #### 密码模式 14 | 15 | [![https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth203.gif](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth203.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth203.gif")](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth203.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth203.gif") 16 | 17 | #### 自定义登录 18 | 19 | [![https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth204.gif](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth204.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth204.gif")](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth204.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth204.gif") 20 | 21 | #### 刷新token 22 | 23 | [![https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth205.gif](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth205.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth205.gif")](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth205.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth205.gif") 24 | 25 | #### 测试资源服务器 26 | 27 | [![https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth206.gif](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth206.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth206.gif")](https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth206.gif "https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth206.gif") 28 | -------------------------------------------------------------------------------- /authorization-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springboot2.0-oauth2 7 | cn.merryyou 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | authorization-server 13 | 14 | 15 | 16 | UTF-8 17 | UTF-8 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-oauth2 25 | 26 | 27 | 28 | org.springframework.security 29 | spring-security-jwt 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-test 36 | 37 | 38 | 39 | org.springframework.boot 40 | spring-boot-starter-redis 41 | 1.4.7.RELEASE 42 | 43 | 44 | 45 | org.projectlombok 46 | lombok 47 | 1.16.14 48 | 49 | 50 | 51 | com.google.code.gson 52 | gson 53 | 2.8.2 54 | 55 | 56 | 57 | io.jsonwebtoken 58 | jjwt 59 | 0.9.0 60 | 61 | 62 | 63 | commons-lang 64 | commons-lang 65 | 2.6 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | org.springframework.boot 74 | spring-boot-maven-plugin 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/SpringBoot2Oauth2Application.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security; 2 | 3 | import cn.merryyou.security.properties.OAuth2Properties; 4 | import cn.merryyou.security.utils.JsonUtil; 5 | import io.jsonwebtoken.Claims; 6 | import io.jsonwebtoken.Jwts; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.apache.commons.lang.StringUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.boot.SpringApplication; 11 | import org.springframework.boot.autoconfigure.SpringBootApplication; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.web.bind.annotation.GetMapping; 14 | import org.springframework.web.bind.annotation.RestController; 15 | 16 | import javax.servlet.http.HttpServletRequest; 17 | import java.io.UnsupportedEncodingException; 18 | import java.security.Principal; 19 | 20 | /** 21 | * Created on 2018/4/28. 22 | * 23 | * @author zlf 24 | * @since 1.0 25 | */ 26 | @RestController 27 | @SpringBootApplication 28 | @Slf4j 29 | public class SpringBoot2Oauth2Application { 30 | 31 | @Autowired 32 | private OAuth2Properties oAuth2Properties; 33 | 34 | 35 | public static void main(String[] args) { 36 | SpringApplication.run(SpringBoot2Oauth2Application.class, args); 37 | } 38 | 39 | @GetMapping("/userJwt") 40 | public Object getCurrentUserJwt(Authentication authentication, HttpServletRequest request) throws UnsupportedEncodingException { 41 | log.info("【SecurityOauth2Application】 getCurrentUserJwt authentication={}", JsonUtil.toJson(authentication)); 42 | 43 | String header = request.getHeader("Authorization"); 44 | String token = StringUtils.substringAfter(header, "bearer "); 45 | 46 | Claims claims = Jwts.parser().setSigningKey(oAuth2Properties.getJwtSigningKey().getBytes("UTF-8")).parseClaimsJws(token).getBody(); 47 | String blog = (String) claims.get("blog"); 48 | log.info("【SecurityOauth2Application】 getCurrentUser1 blog={}", blog); 49 | 50 | return authentication; 51 | } 52 | 53 | @GetMapping("/userRedis") 54 | public Object getCurrentUserRedis(Authentication authentication) { 55 | log.info("【SecurityOauth2Application】 getCurrentUserRedis authentication={}", JsonUtil.toJson(authentication)); 56 | 57 | 58 | return authentication; 59 | } 60 | 61 | @GetMapping("/user/me") 62 | public Principal user(Principal user){ 63 | return user; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/config/TokenStoreConfig.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.config; 2 | 3 | import cn.merryyou.security.properties.OAuth2Properties; 4 | import cn.merryyou.security.security.jwt.MerryyouJwtTokenEnhancer; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.data.redis.connection.RedisConnectionFactory; 11 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 12 | import org.springframework.security.oauth2.provider.token.TokenStore; 13 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 14 | import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; 15 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; 16 | 17 | /** 18 | * Created on 2018/1/21 0021. 19 | * 20 | * @author zlf 21 | * @email i@merryyou.cn 22 | * @since 1.0 23 | */ 24 | @Configuration 25 | public class TokenStoreConfig { 26 | /** 27 | * redis连接工厂 28 | */ 29 | @Autowired 30 | private RedisConnectionFactory redisConnectionFactory; 31 | 32 | 33 | /** 34 | * 用于存放token 35 | * @return 36 | */ 37 | @Bean 38 | @ConditionalOnProperty(prefix = "merryyou.security.oauth2", name = "storeType", havingValue = "redis") 39 | public TokenStore redisTokenStore() { 40 | return new RedisTokenStore(redisConnectionFactory); 41 | } 42 | 43 | /** 44 | * jwt TOKEN配置信息 45 | */ 46 | @Configuration 47 | @ConditionalOnProperty(prefix = "merryyou.security.oauth2", name = "storeType", havingValue = "jwt", matchIfMissing = true) 48 | public static class JwtTokenCofnig{ 49 | 50 | @Autowired 51 | private OAuth2Properties oAuth2Properties; 52 | 53 | /** 54 | * 使用jwtTokenStore存储token 55 | * @return 56 | */ 57 | @Bean 58 | public TokenStore jwtTokenStore(){ 59 | return new JwtTokenStore(jwtAccessTokenConverter()); 60 | } 61 | 62 | /** 63 | * 用于生成jwt 64 | * @return 65 | */ 66 | @Bean 67 | public JwtAccessTokenConverter jwtAccessTokenConverter(){ 68 | JwtAccessTokenConverter accessTokenConverter = new JwtAccessTokenConverter(); 69 | accessTokenConverter.setSigningKey(oAuth2Properties.getJwtSigningKey());//生成签名的key 70 | return accessTokenConverter; 71 | } 72 | 73 | /** 74 | * 用于扩展JWT 75 | * @return 76 | */ 77 | @Bean 78 | @ConditionalOnMissingBean(name = "jwtTokenEnhancer") 79 | public TokenEnhancer jwtTokenEnhancer(){ 80 | return new MerryyouJwtTokenEnhancer(); 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/handler/AppLoginInSuccessHandler.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.handler; 2 | 3 | import cn.merryyou.security.utils.JsonUtil; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.security.authentication.BadCredentialsException; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.crypto.codec.Base64; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 12 | import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException; 13 | import org.springframework.security.oauth2.provider.*; 14 | import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; 15 | import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler; 16 | import org.springframework.stereotype.Component; 17 | 18 | import javax.servlet.ServletException; 19 | import javax.servlet.http.HttpServletRequest; 20 | import javax.servlet.http.HttpServletResponse; 21 | import java.io.IOException; 22 | import java.util.HashMap; 23 | 24 | /** 25 | * APP登录成功处理器 26 | * Created on 2018/1/21 0021. 27 | * 28 | * @author zlf 29 | * @email i@merryyou.cn 30 | * @since 1.0 31 | */ 32 | 33 | @Slf4j 34 | @Component("appLoginInSuccessHandler") 35 | public class AppLoginInSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { 36 | 37 | @Autowired 38 | private ObjectMapper objectMapper; 39 | 40 | @Autowired 41 | private PasswordEncoder passwordEncoder; 42 | 43 | @Autowired 44 | private ClientDetailsService clientDetailsService; 45 | 46 | @Autowired 47 | private AuthorizationServerTokenServices authorizationServerTokenServices; 48 | 49 | @Override 50 | public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws ServletException, IOException { 51 | 52 | log.info("【AppLoginInSuccessHandler】 onAuthenticationSuccess authentication={}", authentication); 53 | 54 | String header = request.getHeader("Authorization"); 55 | 56 | if (header == null || !header.startsWith("Basic ")) { 57 | throw new UnapprovedClientAuthenticationException("请求头中无client信息"); 58 | } 59 | String[] tokens = this.extractAndDecodeHeader(header, request); 60 | 61 | assert tokens.length == 2; 62 | 63 | String clientId = tokens[0]; 64 | String clientSecret = tokens[1]; 65 | 66 | ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); 67 | 68 | if (clientDetails == null) { 69 | throw new UnapprovedClientAuthenticationException("clientId 对应的配置信息不存在" + clientId); 70 | } else if (!passwordEncoder.matches(clientSecret, clientDetails.getClientSecret())) { 71 | throw new UnapprovedClientAuthenticationException("clientSecret 不匹配" + clientId); 72 | } 73 | 74 | TokenRequest tokenRequest = new TokenRequest(new HashMap<>(), clientId, clientDetails.getScope(), "custom"); 75 | 76 | OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); 77 | 78 | OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); 79 | 80 | OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); 81 | 82 | response.setContentType("application/json;charset=UTF-8"); 83 | response.getWriter().write(objectMapper.writeValueAsString(token)); 84 | log.info("token={}", JsonUtil.toJson(token)); 85 | 86 | } 87 | 88 | /** 89 | * 解码 90 | * 91 | * @param header 92 | * @param request 93 | * @return 94 | * @throws IOException 95 | */ 96 | private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { 97 | byte[] base64Token = header.substring(6).getBytes("UTF-8"); 98 | 99 | byte[] decoded; 100 | try { 101 | decoded = Base64.decode(base64Token); 102 | } catch (IllegalArgumentException var7) { 103 | throw new BadCredentialsException("Failed to decode basic authentication token"); 104 | } 105 | 106 | String token = new String(decoded, "UTF-8"); 107 | int delim = token.indexOf(":"); 108 | if (delim == -1) { 109 | throw new BadCredentialsException("Invalid basic authentication token"); 110 | } else { 111 | return new String[]{token.substring(0, delim), token.substring(delim + 1)}; 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/properties/OAuth2ClientProperties.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.properties; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * Created on 2018/1/24 0024. 7 | * 8 | * @author zlf 9 | * @email i@merryyou.cn 10 | * @since 1.0 11 | */ 12 | @Data 13 | public class OAuth2ClientProperties { 14 | 15 | private String clientId; 16 | 17 | private String clientSecret; 18 | 19 | private Integer accessTokenValiditySeconds = 7200; 20 | } 21 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/properties/OAuth2CoreConfig.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.properties; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * Created on 2018/1/24 0024. 8 | * 9 | * @author zlf 10 | * @email i@merryyou.cn 11 | * @since 1.0 12 | */ 13 | @Configuration 14 | @EnableConfigurationProperties(OAuth2Properties.class) 15 | public class OAuth2CoreConfig { 16 | } 17 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/properties/OAuth2Properties.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.properties; 2 | 3 | import lombok.Data; 4 | import org.springframework.boot.context.properties.ConfigurationProperties; 5 | 6 | /** 7 | * Created on 2018/1/24 0024. 8 | * 9 | * @author zlf 10 | * @email i@merryyou.cn 11 | * @since 1.0 12 | */ 13 | @Data 14 | @ConfigurationProperties(prefix = "merryyou.security.oauth2") 15 | public class OAuth2Properties { 16 | 17 | private String jwtSigningKey = "merryyou"; 18 | 19 | private OAuth2ClientProperties[] clients = {}; 20 | } 21 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/security/MyUserDetailsService.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.security; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.security.core.authority.AuthorityUtils; 5 | import org.springframework.security.core.userdetails.User; 6 | import org.springframework.security.core.userdetails.UserDetails; 7 | import org.springframework.security.core.userdetails.UserDetailsService; 8 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 9 | import org.springframework.security.crypto.password.PasswordEncoder; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * Created on 2018/1/17. 14 | * 15 | * @author zlf 16 | * @since 1.0 17 | */ 18 | @Component 19 | public class MyUserDetailsService implements UserDetailsService { 20 | 21 | @Autowired 22 | private PasswordEncoder passwordEncoder; 23 | 24 | @Override 25 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 26 | return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/security/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.security;//package cn.merryyou.security.security; 2 | 3 | 4 | import org.springframework.context.annotation.Bean; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.security.authentication.AuthenticationManager; 7 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | 12 | /** 13 | * Created on 2018/1/19. 14 | * 15 | * @author zlf 16 | * @since 1.0 17 | */ 18 | @Configuration 19 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 20 | 21 | @Bean 22 | @Override 23 | public AuthenticationManager authenticationManagerBean() throws Exception { 24 | AuthenticationManager manager = super.authenticationManagerBean(); 25 | return manager; 26 | } 27 | 28 | @Bean 29 | public PasswordEncoder passwordEncoder() { 30 | return new BCryptPasswordEncoder(); 31 | } 32 | 33 | @Override 34 | protected void configure(HttpSecurity http) throws Exception { 35 | http 36 | // .formLogin().and() 37 | .httpBasic().and() 38 | .csrf().disable(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/security/jwt/MerryyouJwtTokenEnhancer.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.security.jwt; 2 | 3 | import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken; 4 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 5 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 6 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 7 | 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | /** 12 | * Created on 2018/1/21 0021. 13 | * 14 | * @author zlf 15 | * @email i@merryyou.cn 16 | * @since 1.0 17 | */ 18 | public class MerryyouJwtTokenEnhancer implements TokenEnhancer { 19 | @Override 20 | public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { 21 | Map info = new HashMap<>(); 22 | info.put("blog", "https://longfeizheng.github.io/"); 23 | ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(info); 24 | return accessToken; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/server/MerryyouAuthorizationServerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.server; 2 | 3 | import cn.merryyou.security.properties.OAuth2ClientProperties; 4 | import cn.merryyou.security.properties.OAuth2Properties; 5 | import org.apache.commons.lang.ArrayUtils; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.security.authentication.AuthenticationManager; 9 | import org.springframework.security.core.userdetails.UserDetailsService; 10 | import org.springframework.security.crypto.password.PasswordEncoder; 11 | import org.springframework.security.oauth2.config.annotation.builders.InMemoryClientDetailsServiceBuilder; 12 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 13 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 14 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 15 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 16 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 17 | import org.springframework.security.oauth2.provider.token.TokenEnhancer; 18 | import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; 19 | import org.springframework.security.oauth2.provider.token.TokenStore; 20 | import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 21 | 22 | import java.util.ArrayList; 23 | import java.util.List; 24 | 25 | /** 26 | * Created on 2018/1/15 0015. 27 | * 28 | * @author zlf 29 | * @email i@merryyou.cn 30 | * @since 1.0 31 | */ 32 | @Configuration 33 | @EnableAuthorizationServer 34 | public class MerryyouAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { 35 | 36 | @Autowired 37 | private OAuth2Properties oAuth2Properties; 38 | 39 | @Autowired 40 | private AuthenticationManager authenticationManager; 41 | 42 | @Autowired 43 | private UserDetailsService userDetailsService; 44 | 45 | @Autowired 46 | private TokenStore tokenStore; 47 | 48 | @Autowired(required = false) 49 | private JwtAccessTokenConverter jwtAccessTokenConverter; 50 | 51 | @Autowired(required = false) 52 | private TokenEnhancer jwtTokenEnhancer; 53 | 54 | @Autowired 55 | private PasswordEncoder passwordEncoder; 56 | 57 | @Override 58 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { 59 | endpoints.tokenStore(tokenStore) 60 | .authenticationManager(authenticationManager) 61 | .userDetailsService(userDetailsService); 62 | //扩展token返回结果 63 | if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) { 64 | TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); 65 | List enhancerList = new ArrayList(); 66 | enhancerList.add(jwtTokenEnhancer); 67 | enhancerList.add(jwtAccessTokenConverter); 68 | tokenEnhancerChain.setTokenEnhancers(enhancerList); 69 | //jwt 70 | endpoints.tokenEnhancer(tokenEnhancerChain) 71 | .accessTokenConverter(jwtAccessTokenConverter); 72 | } 73 | } 74 | 75 | /** 76 | * 配置客户端一些信息 77 | * 78 | * @param clients 79 | * @throws Exception 80 | */ 81 | @Override 82 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 83 | InMemoryClientDetailsServiceBuilder build = clients.inMemory(); 84 | if (ArrayUtils.isNotEmpty(oAuth2Properties.getClients())) { 85 | for (OAuth2ClientProperties config : oAuth2Properties.getClients()) { 86 | build.withClient(config.getClientId()) 87 | .secret(passwordEncoder.encode(config.getClientSecret())) 88 | .accessTokenValiditySeconds(config.getAccessTokenValiditySeconds()) 89 | .refreshTokenValiditySeconds(60 * 60 * 24 * 15) 90 | .authorizedGrantTypes("refresh_token", "password", "authorization_code")//OAuth2支持的验证模式 91 | .redirectUris("http://www.merryyou.cn") 92 | .scopes("all"); 93 | } 94 | } 95 | } 96 | 97 | // @Override 98 | // public void configure(AuthorizationServerSecurityConfigurer oauthServer) { 99 | // //允许表单认证 100 | // oauthServer.allowFormAuthenticationForClients(); 101 | // oauthServer.passwordEncoder(passwordEncoder); 102 | // } 103 | 104 | /** 105 | * springSecurity 授权表达式, 106 | * @param security 107 | * @throws Exception 108 | */ 109 | @Override 110 | public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { 111 | security.tokenKeyAccess("permitAll()"); 112 | security.checkTokenAccess("isAuthenticated()"); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/server/MerryyouResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.server; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 7 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 8 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 9 | 10 | /** 11 | * Created on 2018/1/17. 12 | * 13 | * @author zlf 14 | * @since 1.0 15 | */ 16 | @Configuration 17 | @EnableResourceServer 18 | public class MerryyouResourceServerConfig extends ResourceServerConfigurerAdapter { 19 | 20 | /** 21 | * 自定义登录成功处理器 22 | */ 23 | @Autowired 24 | private AuthenticationSuccessHandler appLoginInSuccessHandler; 25 | 26 | @Override 27 | public void configure(HttpSecurity http) throws Exception { 28 | http.formLogin() 29 | .successHandler(appLoginInSuccessHandler)//登录成功处理器 30 | .and() 31 | .authorizeRequests().anyRequest().authenticated().and() 32 | .csrf().disable(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /authorization-server/src/main/java/cn/merryyou/security/utils/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | 6 | /** 7 | * Created on 2018/1/17. 8 | * 9 | * @author zlf 10 | * @since 1.0 11 | */ 12 | public class JsonUtil { 13 | public static String toJson(Object object){ 14 | GsonBuilder gsonBuilder = new GsonBuilder(); 15 | gsonBuilder.setPrettyPrinting(); 16 | Gson gson = gsonBuilder.create(); 17 | return gson.toJson(object); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /authorization-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8888 3 | spring: 4 | redis: 5 | host: localhost 6 | port: 6379 7 | jedis: 8 | pool: 9 | max-active: 8 10 | max-wait: -1s 11 | min-idle: 0 12 | max-idle: 8 13 | logging: 14 | level: 15 | org.springframework: debug 16 | merryyou: 17 | security: 18 | oauth2: 19 | storeType: jwt #或者jwt 20 | jwtSigningKey: merryyou 21 | clients[0]: 22 | clientId: merryyou 23 | clientSecret: merryyou 24 | clients[1]: 25 | clientId: merryyou1 26 | clientSecret: merryyou1 27 | -------------------------------------------------------------------------------- /authorization-server/src/test/java/cn/merryyou/security/SpringBoot2Oauth2Test.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security; 2 | 3 | import cn.merryyou.security.utils.JsonUtil; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.codec.binary.Base64; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.boot.test.context.SpringBootTest; 9 | import org.springframework.http.*; 10 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 11 | import org.springframework.test.context.junit4.SpringRunner; 12 | import org.springframework.util.LinkedMultiValueMap; 13 | import org.springframework.util.MultiValueMap; 14 | import org.springframework.web.client.RestTemplate; 15 | 16 | /** 17 | * Created on 2018/4/28. 18 | * 19 | * @author zlf 20 | * @since 1.0 21 | */ 22 | @RunWith(SpringRunner.class) 23 | @SpringBootTest 24 | @Slf4j 25 | public class SpringBoot2Oauth2Test { 26 | //端口 27 | final static long PORT = 8888; 28 | //clientId 29 | final static String CLIENT_ID = "merryyou"; 30 | //clientSecret 31 | final static String CLIENT_SECRET = "merryyou"; 32 | //用户名 33 | final static String USERNAME = "admin"; 34 | //密码 35 | final static String PASSWORD = "123456"; 36 | //获取accessToken得URI 37 | final static String TOKEN_REQUEST_URI = "http://127.0.0.1:" + PORT + "/oauth/token?grant_type=password&username=" + USERNAME + "&password=" + PASSWORD + "&scope=all"; 38 | //获取用户信息得URL 39 | final static String USER_INFO_URI = "http://127.0.0.1:" + PORT + "/userRedis"; 40 | //登录地址 41 | final static String SIGN_IN_URI = "http://127.0.0.1:" + PORT + "/login"; 42 | 43 | @Test 44 | public void getUserInfo() throws Exception { 45 | RestTemplate rest = new RestTemplate(); 46 | HttpHeaders headers = new HttpHeaders(); 47 | headers.add("authorization", "bearer " + getAccessToken()); 48 | HttpEntity entity = new HttpEntity(null, headers); 49 | // pay attention, if using get with headers, should use exchange instead of getForEntity / getForObject 50 | ResponseEntity result = rest.exchange(USER_INFO_URI, HttpMethod.GET, entity, String.class, new Object[]{null}); 51 | log.info("用户信息返回的结果={}", JsonUtil.toJson(result)); 52 | } 53 | 54 | /** 55 | * 用户名密码登录 56 | * @throws Exception 57 | */ 58 | @Test 59 | public void signInTest() throws Exception { 60 | RestTemplate rest = new RestTemplate(); 61 | HttpHeaders headers = new HttpHeaders(); 62 | headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 63 | headers.add("authorization", getBasicAuthHeader()); 64 | 65 | MultiValueMap params = new LinkedMultiValueMap<>(); 66 | params.add("username", "admin"); 67 | params.add("password", "123456"); 68 | 69 | HttpEntity entity = new HttpEntity(params, headers); 70 | // pay attention, if using get with headers, should use exchange instead of getForEntity / getForObject 71 | ResponseEntity result = rest.exchange(SIGN_IN_URI, HttpMethod.POST, entity, String.class, new Object[]{null}); 72 | log.info("登录信息返回的结果={}", JsonUtil.toJson(result)); 73 | } 74 | 75 | /** 76 | * 获取accessToken 77 | * 78 | * @return 79 | */ 80 | private String getAccessToken() { 81 | RestTemplate rest = new RestTemplate(); 82 | HttpHeaders headers = new HttpHeaders(); 83 | headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); 84 | headers.add("authorization", getBasicAuthHeader()); 85 | HttpEntity entity = new HttpEntity(null, headers); 86 | ResponseEntity resp = rest.postForEntity(TOKEN_REQUEST_URI, entity, OAuth2AccessToken.class); 87 | if (!resp.getStatusCode().equals(HttpStatus.OK)) { 88 | throw new RuntimeException(resp.toString()); 89 | } 90 | OAuth2AccessToken t = resp.getBody(); 91 | log.info("accessToken={}", JsonUtil.toJson(t)); 92 | log.info("the response, access_token: " + t.getValue() + "; token_type: " + t.getTokenType() + "; " 93 | + "refresh_token: " + t.getRefreshToken() + "; expiration: " + t.getExpiresIn() + ", expired when:" + t.getExpiration()); 94 | return t.getValue(); 95 | 96 | } 97 | 98 | /** 99 | * 构建header 100 | * 101 | * @return 102 | */ 103 | private String getBasicAuthHeader() { 104 | String auth = CLIENT_ID + ":" + CLIENT_SECRET; 105 | byte[] encodedAuth = Base64.encodeBase64(auth.getBytes()); 106 | String authHeader = "Basic " + new String(encodedAuth); 107 | return authHeader; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cn.merryyou 8 | springboot2.0-oauth2 9 | pom 10 | 1.0-SNAPSHOT 11 | 12 | authorization-server 13 | resource-server 14 | 15 | 16 | 17 | 18 | 19 | io.spring.platform 20 | platform-bom 21 | Cairo-RELEASE 22 | pom 23 | import 24 | 25 | 26 | org.springframework.cloud 27 | spring-cloud-dependencies 28 | Finchley.RC1 29 | pom 30 | import 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-compiler-plugin 40 | 2.3.2 41 | 42 | 1.8 43 | 1.8 44 | UTF-8 45 | 46 | 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | 53 | 54 | repackage 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | spring-snapshots 65 | Spring Snapshots 66 | https://repo.spring.io/snapshot 67 | 68 | true 69 | 70 | 71 | 72 | spring-milestones 73 | Spring Milestones 74 | https://repo.spring.io/milestone 75 | 76 | false 77 | 78 | 79 | 80 | 81 | 82 | 83 | spring-snapshots 84 | Spring Snapshots 85 | https://repo.spring.io/snapshot 86 | 87 | true 88 | 89 | 90 | 91 | spring-milestones 92 | Spring Milestones 93 | https://repo.spring.io/milestone 94 | 95 | false 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /resource-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | springboot2.0-oauth2 7 | cn.merryyou 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | resource-server 13 | 14 | 15 | 16 | org.springframework.cloud 17 | spring-cloud-starter-oauth2 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /resource-server/resource-server.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /resource-server/src/main/java/cn/merryyou/security/SpringBoot2Oauth2ResourceApplication.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.security.access.prepost.PreAuthorize; 6 | import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; 7 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RestController; 10 | 11 | /** 12 | * Created on 2018/5/3 0003. 13 | * 14 | * @author zlf 15 | * @email i@merryyou.cn 16 | * @since 1.0 17 | */ 18 | @SpringBootApplication 19 | @RestController 20 | @EnableGlobalMethodSecurity(prePostEnabled = true)//开启注解 21 | public class SpringBoot2Oauth2ResourceApplication extends ResourceServerConfigurerAdapter { 22 | 23 | public static void main(String[] args) { 24 | SpringApplication.run(SpringBoot2Oauth2ResourceApplication.class, args); 25 | } 26 | 27 | @RequestMapping(value = "/api") 28 | @PreAuthorize("hasRole('ROLE_USER')") 29 | public String success() { 30 | return "SUCCESS"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resource-server/src/main/java/cn/merryyou/security/resource/MerryyouResourceServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package cn.merryyou.security.resource; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.http.HttpMethod; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 7 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 8 | 9 | /** 10 | * Created on 2018/5/5 0005. 11 | * 12 | * @author zlf 13 | * @email i@merryyou.cn 14 | * @since 1.0 15 | */ 16 | @Configuration 17 | @EnableResourceServer 18 | public class MerryyouResourceServerConfiguration extends ResourceServerConfigurerAdapter { 19 | 20 | 21 | @Override 22 | public void configure(HttpSecurity http) throws Exception { 23 | http.csrf().disable().authorizeRequests().antMatchers("/**").authenticated().antMatchers(HttpMethod.GET, "/api") 24 | // 拦截用户,必须具有所列权限 25 | .hasAuthority("ROLE_USER"); 26 | } 27 | 28 | 29 | 30 | 31 | } 32 | -------------------------------------------------------------------------------- /resource-server/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | auth-server: http://localhost:8888 # sso-server地址 2 | server: 3 | port: 8889 4 | security: 5 | oauth2: 6 | client: 7 | client-id: merryyou 8 | client-secret: merryyou 9 | user-authorization-uri: ${auth-server}/oauth/authorize #请求认证的地址 10 | access-token-uri: ${auth-server}/oauth/token #请求令牌的地址 11 | resource: 12 | jwt: 13 | key-uri: ${auth-server}/oauth/token_key 14 | user-info-uri: ${auth-server}/user/me 15 | token-info-uri: ${auth-server}/oauth/check_token 16 | logging: 17 | level: 18 | org.springframework: debug --------------------------------------------------------------------------------