spring cloud oauth2.0 鉴权
标签(空格分隔): springcloud oauth 鉴权
[TOC]
1.概述
文章主要是描述 springcloud 的一个鉴权,利用 oauth2.0 和 security 实现,所以也继承了 security 的权限框架,粒度可以达到按钮级别,具体还是要看设计思路
这里会介绍 3 种的 token 存储方式,(基于内存,基于 redis,基于 jwt)
还有两种 client 的配置方式,一种是采用官方数据库进行初始化,另一种是写在代码内存中。
2.实现流程
在这简单的说一下,不说的那么复杂。一个注册中心,一个鉴权的服务(我暂时把用户也放在鉴权服务里了,你若是喜欢也可以单独分出去),一个 A 服务(就几个简单的 controller 接口)。
项目启动后,用户通过/oauth/token 获取到 access_token,然后通过 access_token 可以请求具体接口,在资源服务器(也就是 A 服务)的配置文件中可以加入一个获取用户信息的配置项,这样就可以把用户信息获取得到,进行权限的判断。
暂时没有用网关去做 oauth2.0 的统一验证,我觉得网关还是只做转发路由,具体还是看情况吧。
3.代码实现
3.1 注册中心 eureka
这个没什么好说的,我用的是 springboot2.0.4 版本,cloud 版本为 Finchley.RELEASE
server:
port : 8761
eureka:
instance:
hostname : localhost
client:
registerWithEureka : false
fetchRegistry : false
serviceUrl:
defaultZone : http://${eureka.instance.hostname}:${server.port}/eureka/
3.2 资源服务器
配置文件
spring:
application:
name: oauth-a
mvc:
servlet:
load-on-startup: 1 #初始化加载,不用懒加载
server:
port: 8888
eureka:
client:
serviceUrl:
defaultZone: http://127.0.0.1:8761/eureka/
registry-fetch-interval-seconds: 5
instance:
lease-expiration-duration-in-seconds: 15
lease-renewal-interval-in-seconds: 5
prefer-ip-address: true
instance-id: ${spring.application.name}:${server.port}
logging:
level:
root: info
security:
oauth2:
resource:
user-info-uri: http://127.0.0.1:7777/user/user-me
prefer-token-info: false
主要是看后面一段 security 配置
配置了一个资源访问的地址,通过这个接口可以获取到登录的用户信息,而这个用户里面的信息包含的内容,要看你在登录的时候设置了什么了,具体看下面的鉴权的服务,一般会在里面返回所有角色信息和权限信息,在security的权限框架中,没有把角色和权限分开,都是统一用的一个集合,所以在角色里要默认加一个ROLE的前缀,如(ROLE_ADMIN,ROLE_USER)
。
security.oauth2.resource.user-info-uri:配置userinfo的url地址
security.oauth2.resource.token-info-uri:配置check-token的url地址;
security.oauth2.resource.prefer-token-info=true,如果上面两个都配置了,更倾向于用哪个
然后我们看一下主要的资源服务器配置:
ResourceServerConfig.java
package com.example.oautha.config;
import java.util.HashMap;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.MediaType;
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.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 资源服务配置
*
*/
@EnableResourceServer
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@SuppressWarnings({ "unchecked", "rawtypes", "serial" })
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling()
.authenticationEntryPoint((req, resp, exception) -> {
resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
resp.getWriter().write(new ObjectMapper().writeValueAsString(new HashMap() {{
put("status", 0);
put("error", "没有权限");
}}));
})
.and().authorizeRequests()
.antMatchers("/anon/**").permitAll() // 放开权限的url
.anyRequest().authenticated().and().httpBasic();
http.headers().frameOptions().sameOrigin();
}
}
中间有一个自定义的返回信息设置,看大家喜好,也可以不要。
然后是上面有一个注解 @EnableGlobalMethodSecurity(prePostEnabled = true)
,开启这个注解为 true,才可以使用 security 的权限控制。如下面的 controller
TestController.java
3.3 授权服务器
首先看用户登录这一块:
@Service("userDetailsServiceImpl")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserDao userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if (user == null) {
throw new AuthenticationCredentialsNotFoundException("用户不存在");
}
return user;
}
}
这是重写了框架自带的一个加载方法,密码校验框架底层做了判断,哦我们只要把信息输入进去就行,而角色和权限信息在此时也需要设置进去,在这里的 User 类是实现了 UserDetails 接口的,如:
@Data
public class User implements UserDetails{
/**
*
*/
private static final long serialVersionUID = 1L;
private Long id;
private String username;
private String password;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//默认给一个用户角色,生产环境自己添加相应的角色和权限
return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER");
}
@Override
public boolean isAccountNonExpired() {
//判断用户是否过期,默认为true,生产环境视情况修改,赋值
return true;
}
@Override
public boolean isAccountNonLocked() {
//判断用户是否被锁定,默认为true,生产环境视情况修改,赋值
return true;
}
@Override
public boolean isCredentialsNonExpired() {
//判断用户凭证是否过期,默认为true,生产环境视情况修改,赋值
return true;
}
@Override
public boolean isEnabled() {
//判断用户是否启用,默认为true,生产环境视情况修改,赋值
return true;
}
}
然后我们看最重要的授权配置
3.3.1 内存存储
3.3.2jwt 存储
3.3.3redis 存储
SecurityConfig.java
主要看这里
@GetMapping("/user-me")
public Principal user(Principal member) {
//获取当前用户信息
return member;
}
4.演示效果
这里我用的是 postman 模拟请求
4.1 请求授权服务
1.请求 token
2.刷新 token
4.2 请求 A 服务
1.请求 anon 公开接口
2.请求 test2 接口(拥有 admin 角色才可以)
2.请求 test1 接口(拥有 user 角色才可以)
5.添加 zuul 网关
zuul 网关最好的就是只做路由,具体的权限还是由各自的资源服务器处理,个人理解,不喜勿喷
@Configuration
@EnableOAuth2Sso
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
}
}
spring:
application:
name: oauth-zuul
server:
port: 9030
eureka:
instance:
prefer-ip-address: true #使用IP注册
instance-id: ${spring.application.name}:${server.port}
client:
service-url:
defaultZone: http://localhost:8761/eureka/
zuul:
host:
connect-timeout-millis: 10000
socket-timeout-millis: 60000
routes:
oauth-center:
path: /uaa/**
strip-prefix: true
sensitiveHeaders:
serviceId: oauth-center
oauth-a:
path: /oa/**
strip-prefix: true
sensitiveHeaders:
serviceId: oauth-a
security:
oauth2:
client:
access-token-uri: http://localhost:9030/uaa/oauth/token ##网关的地址
user-authorization-uri: http://localhost:9030/uaa/oauth/authorize
#client-id: system #OAuth2客户端ID
#client-secret: system #OAuth2客户端密钥
resource:
user-info-uri: http://localhost:9030/uaa/user/user-me
prefer-token-info: false
#jwt:
#key-value: 0123456789 #使
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于