- 在实现SSO之前,先要说一下**@EnableResourceServer与@EnableOAuth2Sso**
- 一般网关处我们使用EnableResourceServer时,可以支持任意的oauth2授权模型,网关声明称资源服务器可以配置ResourceServerConfigurerAdapter,我们可以配置放行的路径等等内容。
- 如果网关处使用EnableOAuth2Sso,在所有请求转发之前就首先要到认证服务器上去校验信息,使用授权码模式,clientid与clientsecret来确认进行oauth2的登陆,跳转登陆页面在进行登陆。
下面我们来简单搭建一个SSO的小项目
-
搭建认证服务器
我们将客户端信息存储在mysql中,token使用jwt。
依赖包只需要oauth2,我cloud用的版本是Greenwich.SR2
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
-
配置AuthorizationServerConfigurerAdapter
package cn.hs.sys.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 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.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import javax.sql.DataSource; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:46 下午 */ @Configuration @EnableAuthorizationServer public class OAuth2AuthServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private DataSource dataSource; @Autowired private BCryptPasswordEncoder passwordEncoder; @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(tokenStore()) .tokenEnhancer(jwtAccessTokenConverter()) .authenticationManager(authenticationManager); } @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey("123456"); return converter; } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { System.out.println("------"+passwordEncoder.encode("123456")); clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("isAuthenticated()") .checkTokenAccess("isAuthenticated()"); } }
-
配置WebSecurityConfigurerAdapter
package cn.hs.sys.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:47 下午 */ @Configuration @EnableWebSecurity public class OAuth2WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService) .passwordEncoder(passwordEncoder); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } }
-
配置UserDetailsService(这里自己实现逻辑返回UserDetails即可)
package cn.hs.sys.config; import cn.hs.sys.Constant; import cn.hs.sys.dao.SysUserEntity; import cn.hs.sys.dao.UserPermissionRepository; import org.apache.commons.lang.StringUtils; import org.springframework.security.core.GrantedAuthority; 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.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @description: * @Author: huangsan * @Date: 2020/5/8 4:53 下午 */ @Service public class UserDetailsServiceImpl implements UserDetailsService { @Resource private UserPermissionRepository userPermissionRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if (StringUtils.isBlank(username)) { throw new UsernameNotFoundException("账号错误"); } SysUserEntity sysUserEntity = userPermissionRepository.findUserIdByName(username); if (sysUserEntity == null) { throw new UsernameNotFoundException("账号异常"); } if (Constant.USER_OFF.equals(sysUserEntity.getUserStatus())) { throw new UsernameNotFoundException("账号状态异常"); } if (sysUserEntity.getUserId() != null) { List<String> permissionStrs = userPermissionRepository.listPermissionById(sysUserEntity.getUserId()); List<GrantedAuthority> grantedAuthorities = new ArrayList<>(); permissionStrs.forEach(permissionStr -> { if (StringUtils.isNotBlank(permissionStr)) { GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionStr); grantedAuthorities.add(grantedAuthority); } }); //密码加密方式为:passwordEncoder.encode() return new User(username, sysUserEntity.getUserPassword(), grantedAuthorities); } else { throw new UsernameNotFoundException("账号异常"); } } }
-
配置文件如下(没什么东西,主要就是mysql的内容,eureka自行去掉吧)
server: port: 52188 spring: application: name: hs-auth datasource: url: jdbc:mysql://localhost:3306/hs_cloud?characterEncoding=utf8&characterSetResults=utf8&autoReconnect=true&useSSL=false&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver eureka: client: service-url: defaultZone: http://127.0.0.1:52199/eureka
-
认证服务器的内容基本都是一样,没什么特别的地方我就直接贴代码了,对于客户端来说有一些需要注意的地方。
-
配置客户端
注意客户端不能使用EnableResourceServer注解
-
引入相关依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency>
-
在启动类上增加@EnableOAuth2Sso注解
-
创建测试web测试类,返回数据
-
配置yml文件
server: port: 52109 servlet: context-path: /sys #特别需要配置,如果是/会反复跳转登陆页面 security: oauth2: resource: jwt: key-uri: http://localhost:52188/oauth/token_key client: client-id: hs-system #客户端id client-secret: 123456 #客户端口令 access-token-uri: http://localhost:52188/oauth/token #获取令牌路径 user-authorization-uri: http://localhost:52188/oauth/authorize #获取授权路径
-
-
相同的方式配置另一个客户端
和第一个客户端一样,注意更改path
-
配置完客户端需要配置登陆跳转路径和自动授权(下面是sql插入语句自行修改)
INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-gateway', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52100/zuul/login', NULL, 3600, 360000, NULL, 'read'); INSERT INTO `hs_cloud`.`oauth_client_details`(`client_id`, `resource_ids`, `client_secret`, `scope`, `authorized_grant_types`, `web_server_redirect_uri`, `authorities`, `access_token_validity`, `refresh_token_validity`, `additional_information`, `autoapprove`) VALUES ('hs-modules-back', NULL, '$2a$10$p/siIwq2lUsOpg6gZC1V5epmeLMdO6BrfKnO36.5ZCn9eHOcibiUi', 'read', 'authorization_code,refresh_token,password', 'http://localhost:52110/back/login', NULL, 3600, 360000, NULL, 'read');
主要注意web_server_redirect_uri与autoapprove,配置autoapprove可以跳过授权的步骤,直接授权
-
成果演示(文字描述是:俩个客户端访问的时候都会去授权服务器,如果没有登陆会跳转登陆页,当一个客户端登录后,可直接访问,不需登陆。视频传上来好麻烦,就不传了,手动狗头。)