SpringCloud |
您所在的位置:网站首页 › oauth2单点登录如何拿到授权码 › SpringCloud |
OAuth2.0提供了四种授权模式 ,refresh_token, 是否刷新token 授权码模式 authorization_code 密码模式 password 简化模式 implicit 客户端模式 client_credentials 此案例目前完成授权码模式,其他模式还没测试,后续会完善 学习笔记,直接上代码了 创建springboot项目,引入基本的依赖,mysql,mybatis,以及自动生成代码jar springcloud版本 Greenwich.BUILD-SNAPSHOT 1. pom文件核心依赖 org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.cloud spring-cloud-starter-security org.springframework.security spring-security-jwt 1.0.10.RELEASE org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test mysql mysql-connector-java org.mybatis.generator mybatis-generator-core 1.3.5 org.mybatis.spring.boot mybatis-spring-boot-starter 1.3.22.首先看spring-security的配置类 package com.youdu.distributed.authentication.config; import com.youdu.distributed.authentication.service.UserServiceDetail; 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.BeanIds; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import javax.servlet.http.HttpServletResponse; /** * @author Sir_小三 * @date 2020/1/30--13:01 * AuthorizationServer认证服务配置 */ @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { //使用密码模式,必须要配置) @Autowired private UserServiceDetail userServiceDetail; /** * //配置认证管理器(使用密码模式,必须要配置) * 但是目前配置此bean,登陆,直接栈溢出error,还没解决 * @return */ @Override @Bean public AuthenticationManager authenticationManager() throws Exception{ return super.authenticationManagerBean(); } //密码编码器 @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 所有请求都必须经过认证 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() // .exceptionHandling() // .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED)) // .and() .authorizeRequests() .anyRequest() .authenticated() .and() .formLogin();//表单提交方式 //.successForwardUrl("/login/success");//登陆成功以后的controller //.httpBasic(); 远程调用Basic方式, } /** * 认证用户名密码 * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userServiceDetail).passwordEncoder(passwordEncoder()); // auth.inMemoryAuthentication()//内存方式 // .passwordEncoder(passwordEncoder()) // 指定加密方式 // .withUser("lyj").password(passwordEncoder().encode("123456")).roles("ADMIN"); } }3.认证服务的配置类 package com.youdu.distributed.authentication.config; import com.youdu.distributed.authentication.service.UserServiceDetail; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.NoOpPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; 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.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.code.JdbcAuthorizationCodeServices; import org.springframework.security.oauth2.provider.token.*; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import javax.sql.DataSource; import java.util.Arrays; /** * @author Sir_小三 * @date 2020/1/30--14:01 * AuthorizationServer认证服务配置 */ @Configuration @EnableAuthorizationServer//开启认证服务/oauth/authorize,/oauth/token,/oauth/check_token,/oauth/confirm_access,/oauth/error public class AuthorizationServer extends AuthorizationServerConfigurerAdapter { // @Autowired // private AuthenticationManager authenticationManager; @Autowired private TokenStore tokenStore; @Autowired private AuthorizationCodeServices authorizationCodeServices; @Autowired private JwtAccessTokenConverter jwtAccessTokenConverter; @Autowired private PasswordEncoder passwordEncoder; @Autowired private DataSource dataSource; // @Autowired // private UserServiceDetail userServiceDetail; /** * 配置令牌访问端点得安全约束 * 必须设置allowFormAuthenticationForClients 否则没有办法用postman获取token * 也需要指定密码加密方式BCryptPasswordEncoder * */ public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.tokenKeyAccess("permitAll()") //公开获取token的url .checkTokenAccess("permitAll()")//校验token的合法性 .allowFormAuthenticationForClients(); //.passwordEncoder(NoOpPasswordEncoder.getInstance());//不需要加密 } /** * //配置客户端详情,客户端详情可以通过数据库查询,(校验哪些客户端可以申请令牌) * clientid 客户端id * secret 客户端安全码 * scope 用来限制客户端得访问范围,默认为空,如果为空,那么客户端拥有全部得访问范围 * authorizedGrantTypes 此客户端可以使用得授权类型,默认为空,oauth2提供了五种授权类型 * authorities 此客户端可以使用 得权限 基于spring security authorities(一般不使用) * * @param clients * @throws Exception * 这里客户端信息存在数据库 */ public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.withClientDetails(clientDetailsService());//配置客户端信息 //内存方式配置 // clients.inMemory().withClient("resources-service")//客户端id // .secret(passwordEncoder.encode("123456"))//客户端密钥,需要加密,使用数据库存,数据库的要经过加密处理 // .resourceIds("res1")//资源id // .authorizedGrantTypes("authorization_code","refresh_token","password")//oauth2 四种授权类型,目前两种,授权码,密码模式 // .scopes("all") // .autoApprove(false)//授权码模式,false会跳转到授权页面,让用户授权,true不跳 // .redirectUris("http://www.baidu.com")//授权码模式,回调地址 // .accessTokenValiditySeconds(3600); } /** * 配置令牌访问端点url,和令牌服务(令牌生成策略,如何生成) * /oauth/authorize 授权端点 * 获取授权码 * http://localhost:8762/oauth/authorize?response_type=code&client_id=resources-service&scope=all&client_secret=123456&redirect_uri=http://www.baidu.com * /oauth/token 获取令牌端点(发送post请求,获取token,code=申请的授权码) * * /oauth/confirm_access 用户确认授权端点 * /oauth/error 授权服务错误信息端点 * /oauth/check_token 用于资源服务访问的令牌解析端点 * /oauth/token_key 提供公有密钥的端点,如果使用jwt令牌的话 * 授权端点url应该被spring-security 保护起来,只供授权用户访问 * @param endpoints * @throws Exception */ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints //.authenticationManager(authenticationManager)//认证管理服务,密码模式必须配置,还有userServiceDetail .authorizationCodeServices(authorizationCodeServices)//授权码模式 .tokenStore(tokenStore) //在这里设置jwtAccessTokenConverter,发现是ok的,可以生成jwt令牌 .accessTokenConverter(jwtAccessTokenConverter) //.userDetailsService(userServiceDetail)//密码模式要配 .allowedTokenEndpointRequestMethods(HttpMethod.POST); } //令牌管理服务 @Bean public AuthorizationServerTokenServices tokenServices() { DefaultTokenServices service = new DefaultTokenServices(); service.setClientDetailsService(clientDetailsService());//客户端详情服务`ervice.setSupportRefreshToken(true);//支持刷新令牌-* service.setTokenStore(tokenStore);//令牌存储策略 //令牌增强,使用jwt令牌 TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); //在这里设置jwtAccessTokenConverter,但是发现不起作用,令牌还是普通令牌,加到发现可以 tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter)); service.setTokenEnhancer(tokenEnhancerChain); service.setSupportRefreshToken(true); //支持刷新令牌 service.setAccessTokenValiditySeconds(7200);//令牌默认有效两小时 service.setRefreshTokenValiditySeconds(259200);//刷新令牌默认有效期三天 return service; } //设置授权码模式的授权码如何存取 @Bean public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource){ return new JdbcAuthorizationCodeServices(dataSource); } //内存方式存储授权码 // @Bean // public AuthorizationCodeServices authorizationCodeServices(){ // return new InMemoryAuthorizationCodeServices(); // } /** * 客户端配置从数据库读取,发起授权时,会查询数据库数据进行认证,可添加n条客户端数据,获取验证码 * 客户端拿到验证码,请求/oauth/token,获取令牌, * 注意!!!数据库中存储客户端密钥,需要使用BCryptPasswordEncoder加密后,在存, * 不然获取token的时候会报错(说不是BCryptPasswordEncoder格式) * (应该是将你请求的密钥123456经过加密,和数据库中的数据做比对,ok,则发送令牌) * 获取token需要的参数 * "client_id":"r1", * "client_secret":"123456", * "grant_type":"authorization_code", * "scope":"all", * "redirect_uri":"http://www.baidu.com", * "code":"5AsJkQ", * @return */ @Bean public ClientDetailsService clientDetailsService(){ JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource); jdbcClientDetailsService.setPasswordEncoder(passwordEncoder); return jdbcClientDetailsService; } }4.JwtTokenConfig 配置类,暂时使用对称加密 package com.youdu.distributed.authentication.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; /** * @author Sir_小三 * @date 2020/1/30--14:34 */ @Configuration public class TokenConfig { //对称加密,密钥,也可采用非对称加密 private String SIGNING_KRY="lyj123"; /** * 使用密钥生成令牌 * @return */ @Bean public JwtAccessTokenConverter accessTokenConverter(){ JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setSigningKey(SIGNING_KRY); return converter; } @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } //内存存储简单令牌 // @Bean // public TokenStore tokenStore() { // return new InMemoryTokenStore(); // } }5.还有一个重要的,UserServiceDetail 配置 这里SecurityUser 去继承security.core.userdetails.User提供的user 此类来验证登陆用户密码 package com.youdu.distributed.authentication.service; import com.youdu.distributed.authentication.entity.SecurityUser; import com.youdu.distributed.authentication.entity.TbUser; import com.youdu.distributed.authentication.mapper.TbUserMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.GrantedAuthority; 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 java.util.ArrayList; import java.util.List; /** * @author Sir_小三 * @date 2020/1/30--12:54 */ @Service public class UserServiceDetail implements UserDetailsService { @Autowired private TbUserMapper tbUserMapper; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { List grantedAuthorities = new ArrayList(); TbUser tbUser = new TbUser(); tbUser.setName(username); List page = tbUserMapper.page(tbUser, null); TbUser tbUser1 = page.get(0); SecurityUser securityUser = new SecurityUser(username, tbUser1.getPassword(),grantedAuthorities); return securityUser; } } package com.youdu.distributed.authentication.entity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; /** * @author Sir_小三 * @date 2020/1/30--18:56 */ public class SecurityUser extends User { public SecurityUser(String username, String password, Collection authorities) { super(username, password, authorities); } public SecurityUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection authorities) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); } }6.到这里重要代码基本就贴完了,剩下的就没什么了,贴一下配置文件,主要就eureka地址,以及mysql连接 #client每30秒会发送心跳给eureka进行续约,如果eureka90秒没有收到客户端心跳,那么注册中心会剔除此client #client注册到eureka时,会将自己的ip地址等信息进行提供,eureka会存所有客户端的注册信息表,client会将其获取到本地内存,进行调用 server.port:8762 #需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name 。 spring.application.name=distributed-authentication #指定erreka-server注册中心的地址 eureka.client.service-url.defaultZone=http://localhost:8761/eureka/ #开启feign断路器熔断机制,Feign是自带断路器的,在D版本的Spring Cloud之后,它没有默认打开。需要在配置文件中配置打开它 #feign.hystrix.enabled=true #指定链路跟踪地址zipkin server #spring.zipkin.base-url=http://localhost:9411 #调用的超时时间 springcloud默认情况下一秒之内调用成功,否则将打开断路器 #hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000 #spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #spring.datasource.url=jdbc:mysql://localhost:3306/rootdatabase?useUnicode=true&characterEncoding=utf-8&useSSL=false #spring.datasource.username=root #spring.datasource.password=123456 mybatis.mapper-locations=classpath:mapper/*.xml #tx-lcn.client.manager-address=127.0.0.1:8070 tc默认连接tm的ip端口 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/oauth2?useUnicode=true&characterEncoding=utf-8&useLegacyDatetimeCode=false&serverTimezone=PRC spring.datasource.username=root spring.datasource.password=123456 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.main.allow-bean-definition-overriding=true7.提供一下oauth2的数据库表,这都是由oauth2提供的,直接运行生成表即可 CREATE SCHEMA IF NOT EXISTS `oauth2` DEFAULT CHARACTER SET utf8 ; USE `oauth2` ; -- ----------------------------------------------------- -- Table `oauth2`.`clientdetails` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`clientdetails` ( `appId` VARCHAR(128) NOT NULL, `resourceIds` VARCHAR(256) NULL DEFAULT NULL, `appSecret` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `grantTypes` VARCHAR(256) NULL DEFAULT NULL, `redirectUrl` VARCHAR(256) NULL DEFAULT NULL, `authorities` VARCHAR(256) NULL DEFAULT NULL, `access_token_validity` INT(11) NULL DEFAULT NULL, `refresh_token_validity` INT(11) NULL DEFAULT NULL, `additionalInformation` VARCHAR(4096) NULL DEFAULT NULL, `autoApproveScopes` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`appId`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_access_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_access_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication_id` VARCHAR(128) NOT NULL, `user_name` VARCHAR(256) NULL DEFAULT NULL, `client_id` VARCHAR(256) NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL, `refresh_token` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`authentication_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_approvals` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_approvals` ( `userId` VARCHAR(256) NULL DEFAULT NULL, `clientId` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `status` VARCHAR(10) NULL DEFAULT NULL, `expiresAt` DATETIME NULL DEFAULT NULL, `lastModifiedAt` DATETIME NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_client_details` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_details` ( `client_id` VARCHAR(128) NOT NULL, `resource_ids` VARCHAR(256) NULL DEFAULT NULL, `client_secret` VARCHAR(256) NULL DEFAULT NULL, `scope` VARCHAR(256) NULL DEFAULT NULL, `authorized_grant_types` VARCHAR(256) NULL DEFAULT NULL, `web_server_redirect_uri` VARCHAR(256) NULL DEFAULT NULL, `authorities` VARCHAR(256) NULL DEFAULT NULL, `access_token_validity` INT(11) NULL DEFAULT NULL, `refresh_token_validity` INT(11) NULL DEFAULT NULL, `additional_information` VARCHAR(4096) NULL DEFAULT NULL, `autoapprove` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`client_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_client_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_client_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication_id` VARCHAR(128) NOT NULL, `user_name` VARCHAR(256) NULL DEFAULT NULL, `client_id` VARCHAR(256) NULL DEFAULT NULL, PRIMARY KEY (`authentication_id`)) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_code` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_code` ( `code` VARCHAR(256) NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8; -- ----------------------------------------------------- -- Table `oatuh2`.`oauth_refresh_token` -- ----------------------------------------------------- CREATE TABLE IF NOT EXISTS `oauth2`.`oauth_refresh_token` ( `token_id` VARCHAR(256) NULL DEFAULT NULL, `token` BLOB NULL DEFAULT NULL, `authentication` BLOB NULL DEFAULT NULL) ENGINE = InnoDB DEFAULT CHARACTER SET = utf8;8.获取授权码url参考 首先发送这个请求,参数要换成自己的,由于spring-security的配置,所有请求必须认证通过,所以会跳至spring-security提供的登陆进行认证,登陆成功,还需认证客户端参数,是否于数据库配置一样,一样,则返回授权页面,进行授权,用户点击授权,跳转到指定url,并发放授权码, 拿到授权码,认证服务器会将授权码存在数据库,一个授权码只能申请一次令牌 接下来,使用授权码获取jwt令牌 http://localhost:8762/oauth/authorize?response_type=code&client_id=resources-service&scope=all&client_secret=123456&redirect_uri=http://www.baidu.com获取到code图片 9.获取到授权码,postman通过获取的授权码,申请jwt令牌 10.校验令牌
11.以后客户端拿令牌,访问资源服务,目前资源服务还没做好,做好后期会更新,还要密码授权模式,也没搞清除,感觉密码模式加jwt可以做分布式的单点登陆,后续有时间会更新的,有不清除的或者想要源码的,可加我微信,xiaosang953038659,谢谢你看到最后,嘻嘻 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |