赞
踩
- <!--导入spring cloud oauth2依赖-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-oauth2</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- <version>2.1.11.RELEASE</version>
- </dependency>
- <!--引入security对oauth2的支持-->
- <dependency>
- <groupId>org.springframework.security.oauth</groupId>
- <artifactId>spring-security-oauth2</artifactId>
- <version>2.3.4.RELEASE</version>
- </dependency>
Yaml配置
- server:
- port: 9999
- Spring:
- application:
- name: cloud-oauth-server
- datasource:
- driver-class-name: com.mysql.jdbc.Driver
- url: jdbc:mysql://XXX:XXX/private_chat?useUnicode=true&characterEncoding=utf8&autoReconnect=true&serverTimezone=GMT%2B8&useSSL=false
- username: XXX
- password: XXX
- druid:
- initialSize: 10
- minIdle: 10
- maxActive: 30
- maxWait: 50000
- eureka:
- client:
- serviceUrl: # eureka server的路径
- defaultZone: http://CloudeurekaserverB:8761/eureka/,http://CloudeurekaserverB:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
- instance:
- #使用ip注册,否则会使用主机名注册了(此处考虑到对老版本的兼容,新版本经过实验都是ip)
- prefer-ip-address: true
- #自定义实例显示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
- instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
OAuth2配置类(基础配置、JWT、JPA框架下的数据库连接获取)
- /**
- * 当前类为Oauth2 server的配置类(需要继承特定的父类 AuthorizationServerConfigurerAdapter)
- */
- @Configuration
- @EnableAuthorizationServer // 开启认证服务器功能
- public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {
-
-
- @Autowired
- private AuthenticationManager authenticationManager;
-
- @Autowired
- private LagouAccessTokenConvertor lagouAccessTokenConvertor;
-
-
- private String sign_key = "lagou123"; // jwt签名密钥
-
-
- /**
- * 认证服务器最终是以api接口的方式对外提供服务(校验合法性并生成令牌、校验令牌等)
- * 那么,以api接口方式对外的话,就涉及到接口的访问权限,我们需要在这里进行必要的配置
- * @param security
- * @throws Exception
- */
- @Override
- public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
- super.configure(security);
- // 相当于打开endpoints 访问接口的开关,这样的话后期我们能够访问该接口
- security
- // 允许客户端表单认证
- .allowFormAuthenticationForClients()
- // 开启端口/oauth/token_key的访问权限(允许)
- .tokenKeyAccess("permitAll()")
- // 开启端口/oauth/check_token的访问权限(允许)
- .checkTokenAccess("permitAll()");
- }
-
- /**
- * 客户端详情配置,
- * 比如client_id,secret
- * 当前这个服务就如同QQ平台,拉勾网作为客户端需要qq平台进行登录授权认证等,提前需要到QQ平台注册,QQ平台会给拉勾网
- * 颁发client_id等必要参数,表明客户端是谁
- * @param clients
- * @throws Exception
- */
- @Override
- public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
- super.configure(clients);
-
-
- // 从内存中加载客户端详情
-
- /*clients.inMemory()// 客户端信息存储在什么地方,可以在内存中,可以在数据库里
- .withClient("client_lagou") // 添加一个client配置,指定其client_id
- .secret("abcxyz") // 指定客户端的密码/安全码
- .resourceIds("autodeliver") // 指定客户端所能访问资源id清单,此处的资源id是需要在具体的资源服务器上也配置一样
- // 认证类型/令牌颁发模式,可以配置多个在这里,但是不一定都用,具体使用哪种方式颁发token,需要客户端调用的时候传递参数指定
- .authorizedGrantTypes("password","refresh_token")
- // 客户端的权限范围,此处配置为all全部即可
- .scopes("all");*/
-
- // 从数据库中加载客户端详情
- clients.withClientDetails(createJdbcClientDetailsService());
-
- }
-
- @Autowired
- private DataSource dataSource;
-
- @Bean
- public JdbcClientDetailsService createJdbcClientDetailsService() {
- JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
- return jdbcClientDetailsService;
- }
-
-
- /**
- * 认证服务器是玩转token的,那么这里配置token令牌管理相关(token此时就是一个字符串,当下的token需要在服务器端存储,
- * 那么存储在哪里呢?都是在这里配置)
- * @param endpoints
- * @throws Exception
- */
- @Override
- public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
- super.configure(endpoints);
- endpoints
- .tokenStore(tokenStore()) // 指定token的存储方法
- .tokenServices(authorizationServerTokenServices()) // token服务的一个描述,可以认为是token生成细节的描述,比如有效时间多少等
- .authenticationManager(authenticationManager) // 指定认证管理器,随后注入一个到当前类使用即可
- .allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
- }
-
-
- /*
- 该方法用于创建tokenStore对象(令牌存储对象)
- token以什么形式存储
- */
- public TokenStore tokenStore(){
- //return new InMemoryTokenStore();
- // 使用jwt令牌
- return new JwtTokenStore(jwtAccessTokenConverter());
- }
-
- /**
- * 返回jwt令牌转换器(帮助我们生成jwt令牌的)
- * 在这里,我们可以把签名密钥传递进去给转换器对象
- * @return
- */
- public JwtAccessTokenConverter jwtAccessTokenConverter() {
- JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
- jwtAccessTokenConverter.setSigningKey(sign_key); // 签名密钥
- jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的密钥,和签名密钥保持一致
- jwtAccessTokenConverter.setAccessTokenConverter(lagouAccessTokenConvertor);
-
- return jwtAccessTokenConverter;
- }
-
-
-
-
- /**
- * 该方法用户获取一个token服务对象(该对象描述了token有效期等信息)
- */
- public AuthorizationServerTokenServices authorizationServerTokenServices() {
- // 使用默认实现
- DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
- defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新
- defaultTokenServices.setTokenStore(tokenStore());
-
- // 针对jwt令牌的添加
- defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
-
- // 设置令牌有效时间(一般设置为2个小时)
- defaultTokenServices.setAccessTokenValiditySeconds(20); // access_token就是我们请求资源需要携带的令牌
- // 设置刷新令牌的有效时间
- defaultTokenServices.setRefreshTokenValiditySeconds(259200); // 3天
-
- return defaultTokenServices;
- }
- }
验证令牌数据库存储情况下的用户名与密码验证类(数据库操作省略)
- /**
- * 该配置类,主要处理用户名和密码的校验等事宜
- */
- @Configuration
- public class SecurityConfiger extends WebSecurityConfigurerAdapter {
-
- @Autowired
- private PasswordEncoder passwordEncoder;
-
- @Autowired
- private JdbcUserDetailsService jdbcUserDetailsService;
-
- /**
- * 注册一个认证管理器对象到容器
- */
- @Bean
- @Override
- public AuthenticationManager authenticationManagerBean() throws Exception {
- return super.authenticationManagerBean();
- }
-
-
- /**
- * 密码编码对象(密码不进行加密处理)
- * @return
- */
- @Bean
- public PasswordEncoder passwordEncoder() {
- return NoOpPasswordEncoder.getInstance();
- }
-
- /**
- * 处理用户名和密码验证事宜
- * 1)客户端传递username和password参数到认证服务器
- * 2)一般来说,username和password会存储在数据库中的用户表中
- * 3)根据用户表中数据,验证当前传递过来的用户信息的合法性
- */
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- // 在这个方法中就可以去关联数据库了,当前我们先把用户信息配置在内存中
- // 实例化一个用户对象(相当于数据表中的一条用户记录)
- /*UserDetails user = new User("admin","123456",new ArrayList<>());
- auth.inMemoryAuthentication()
- .withUser(user).passwordEncoder(passwordEncoder);*/
-
- auth.userDetailsService(jdbcUserDetailsService).passwordEncoder(passwordEncoder);
- }
- }
从认证服务端获取令牌,令牌内容注入配置
- @Component
- public class AccessTokenConvertor extends DefaultAccessTokenConverter {
-
-
- @Override
- public Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
- // 获取到request对象
- HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.getRequestAttributes())).getRequest();
- // 获取客户端ip(注意:如果是经过代理之后到达当前服务的话,那么这种方式获取的并不是真实的浏览器客户端ip)
- String remoteAddr = request.getRemoteAddr();
- Map<String, String> stringMap = (Map<String, String>) super.convertAccessToken(token, authentication);
- stringMap.put("clientIp",remoteAddr);
- return stringMap;
- }
- }
- spring:
- application:
- name: cloud-gateway
- cloud:
- gateway:
- routes: # 路由可以有多个
- - id: service-oauth-router # 我们自定义的路由 ID,保持唯一
- uri: lb://cloud-oauth-server # gateway网关从服务注册中心获取实例信息然后负载后路由
- predicates: # 断言:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默 认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
- - Path=/oauth/**
oauth2基本使用情况下,服务器端对认证服务请求判断自己的获取的令牌是否正确,如果服务端过多会导致认证服务器端压力过大。故通过JWT或者数据库端的令牌持久化来进行自我认证,JWT通过统一签名秘钥自身通过算法来解析是否正确。
- <!--导入spring cloud oauth2依赖-->
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-oauth2</artifactId>
- <exclusions>
- <exclusion>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
- <dependency>
- <groupId>org.springframework.security.oauth.boot</groupId>
- <artifactId>spring-security-oauth2-autoconfigure</artifactId>
- <version>2.1.11.RELEASE</version>
- </dependency>
- <!--引入security对oauth2的支持-->
- <dependency>
- <groupId>org.springframework.security.oauth</groupId>
- <artifactId>spring-security-oauth2</artifactId>
- <version>2.3.4.RELEASE</version>
- </dependency>
- oauth2:
- server:
- check-token-url: http://localhost:9999/oauth/check_token
服务令牌认证配置类
- @Configuration
- @EnableResourceServer // 开启资源服务器功能
- @EnableWebSecurity // 开启web访问安全
- public class ResourceServerConfiger extends ResourceServerConfigurerAdapter {
-
- private String sign_key = "Test123"; // jwt签名密钥
-
- @Autowired
- private LagouAccessTokenConvertor lagouAccessTokenConvertor;
-
- /**
- * 该方法用于定义资源服务器向远程认证服务器发起请求,进行token校验等事宜
- * @param resources
- * @throws Exception
- */
- @Override
- public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
-
- /*// 设置当前资源服务的资源id
- resources.resourceId("autodeliver");
- // 定义token服务对象(token校验就应该靠token服务对象)
- RemoteTokenServices remoteTokenServices = new RemoteTokenServices();
- // 校验端点/接口设置
- remoteTokenServices.setCheckTokenEndpointUrl("http://localhost:9999/oauth/check_token");
- // 携带客户端id和客户端安全码
- remoteTokenServices.setClientId("client_lagou");
- remoteTokenServices.setClientSecret("abcxyz");
- // 别忘了这一步
- resources.tokenServices(remoteTokenServices);*/
-
-
- // jwt令牌改造
- resources.resourceId("autodeliver").tokenStore(tokenStore()).stateless(true);// 无状态设置
- }
-
-
- /**
- * 场景:一个服务中可能有很多资源(API接口)
- * 某一些API接口,需要先认证,才能访问
- * 某一些API接口,压根就不需要认证,本来就是对外开放的接口
- * 我们就需要对不同特点的接口区分对待(在当前configure方法中完成),设置是否需要经过认证
- *
- * @param http
- * @throws Exception
- */
- @Override
- public void configure(HttpSecurity http) throws Exception {
- http // 设置session的创建策略(根据需要创建即可)
- .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
- .and()
- .authorizeRequests()
- .antMatchers("/autodeliver/**").authenticated() // autodeliver为前缀的请求需要认证
- .antMatchers("/demo/**").authenticated() // demo为前缀的请求需要认证
- .anyRequest().permitAll(); // 其他请求不认证
- }
-
-
-
-
- /*
- 该方法用于创建tokenStore对象(令牌存储对象)
- token以什么形式存储
- */
- public TokenStore tokenStore(){
- //return new InMemoryTokenStore();
-
- // 使用jwt令牌
- return new JwtTokenStore(jwtAccessTokenConverter());
- }
-
- /**
- * 返回jwt令牌转换器(帮助我们生成jwt令牌的)
- * 在这里,我们可以把签名密钥传递进去给转换器对象
- * @return
- */
- public JwtAccessTokenConverter jwtAccessTokenConverter() {
- JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
- jwtAccessTokenConverter.setSigningKey(sign_key); // 签名密钥
- jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的密钥,和签名密钥保持一致
- jwtAccessTokenConverter.setAccessTokenConverter(lagouAccessTokenConvertor);
- return jwtAccessTokenConverter;
- }
-
- }
令牌使用
- @Component
- public class AccessTokenConvertor extends DefaultAccessTokenConverter {
-
-
- @Override
- public OAuth2Authentication extractAuthentication(Map<String, ?> map) {
-
- OAuth2Authentication oAuth2Authentication = super.extractAuthentication(map);
- oAuth2Authentication.setDetails(map); // 将map放入认证对象中,认证对象在controller中可以拿到
- return oAuth2Authentication;
- }
- }
控制层代码(两种模式下):
- @GetMapping("/test")
- public String findResumeOpenState() {
- Object details = SecurityContextHolder.getContext().getAuthentication().getDetails();
- return "demo/test!";
- }
-
- @GetMapping("/test")
- public String findResumeOpenState() {
- return "others/test!";
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。