spring boot Security Oauth2.0最简版本集成
搭建最简版本,工具IntelliJ idea,构建方式maven,话不多说,上代码
请同学们理性看待最简开发模式,真实开发涉及的内容非常多,只做思路
pom文件
<!-- security依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<!--web依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
用户实体
为了方便操作,给用户赋权限(上篇使用了Security 的 USER对象,这次换个方式耍耍吧)
/**
* @Title: LoginAppUser
* @description: 用户信息管理
* @author: LIUFANG
* @create: 2020/3/13 12:54
* @Version: v1.0
*/
@Getter
@Setter
public class LoginAppUser implements UserDetails {
private Long id;
private String username;
private String password;
private String nickname;
private String headImgUrl;
private String phone;
private Integer sex;
/**
* 状态
*/
private Boolean enabled;
private String type;
private String deptId;
private String deptName;
private Date createTime;
private Date updateTime;
private Set<String> sysRoles;
private Set<String> permissions;
/**权限的操作直接放在此处了,简单粗暴,如果你开心,你可以把类做的细化一点*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Collection<GrantedAuthority> collection = new HashSet<>();
/**为什么加ROLE_,看了一下源码,你可以理解为:区分资源和权限的标识符*/
collection.add(new SimpleGrantedAuthority("ROLE_"+"USER"));
return collection;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getUsername() {
/**写死的数据,真实开发查询数据库就OK了*/
return "user";
}
UserDetailsService 不解释
/**
* @Title: MyUserDetailsService
* @description: 用户管理
* @author: LIUFANG
* @create: 2020/3/13 9:27
* @Version: v1.0
*/
@Component
public class MyUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder userPasswordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
LoginAppUser loginAppUser = new LoginAppUser();
loginAppUser.setUsername("user");
loginAppUser.setPassword(userPasswordEncoder.encode("123456"));
return loginAppUser;
}
}
WebSecurityConfig
spring security的配置
/**
* @Title: SecurityConfig
* @description: 权限配置
* @author: LIUFANG
* @create: 2020/3/13 9:12
* @Version: v1.0
*/
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService userDetailsService;
/**
* 一定要将 userDetailsService 设置到 AuthenticationManagerBuilder 中
* 不然后面校验ClientDetailsService时会找不到UsernamePasswordToken的Provider
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(userPasswordEncoder());
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("ADMIN","USER")
.antMatchers("/test").authenticated()
.anyRequest().authenticated().and()
/**因为已经加入了其他的认证,此处这个basic咱们就不用了*/
//.httpBasic().and()
.csrf()
.disable();
}
@Bean
public PasswordEncoder userPasswordEncoder(){
return new BCryptPasswordEncoder();
}
/**
* 覆盖WebSecurityConfigurerAdapter类(本类集成的爸爸)authenticationManagerBean的方法,最终要的一点,重写的时候,直接使用super就OK,这波操作给6分
* 注意的是,这个bean不重写,在OauthAuthorizationServerConfig是无法获取的,这个是个小坑
* @return
* @throws Exception
*/
@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception{
return super.authenticationManager();
}
Oauth 配置类
/**
* @Title: OauthAuthorizationServerConfig
* @description: Oauth2.0配置
* @author: LIUFANG
* @create: 2020/3/13 10:22
* @Version: v1.0
*/
@Configuration
@EnableAuthorizationServer
public class OauthAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**这个地方需要注意一下,这个bean必须自行创建出来,参照WebSecurityConfig类*/
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private MyUserDetailsService userDetailsService;
/**
* 配置访问端点和令牌服务
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
/**注意这个allowFormAuthenticationForClients,开启表单验证*/
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
/**
* 用来配置客户端详情服务
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
/**client信息,可以存储数据库,可以redis,也可以存储内存,看你心情*/
clients.inMemory()
.withClient("system")
.secret(passwordEncoder.encode("123456"))
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.scopes("app");
}
/**
* 配置token
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
/**直接存储内存*/
endpoints
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.authenticationManager(authenticationManager)
.tokenStore(new InMemoryTokenStore())
.userDetailsService(userDetailsService);
}
}
ResourceServerConfig资源服务器
这个类的作用是为Oauth请求服务,必须配置
/**
* @Title: ResourceServerConfig
* @description: 资源服务配置
* @author: LIUFANG
* @create: 2020/3/13 13:54
* @Version: v1.0
*/
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/admin/**").hasRole("ADMIN")
.antMatchers("/user/**").hasAnyRole("ADMIN","USER")
.antMatchers("/test").authenticated()
.anyRequest().authenticated();
}
}
controller调用
/**
* @Title: MyController
* @description: 测试Controller
* @author: LIUFANG
* @create: 2020/3/13 10:56
* @Version: v1.0
*/
@RestController
public class MyController {
@GetMapping(value = "/test")
public Object test(){
return "test";
}
@GetMapping(value = "/user")
@PreAuthorize("hasAnyRole('ROLE_USER')")
public Object user(){
return "user";
}
@GetMapping(value = "/user/a")
public Object user2(){
return "user";
}
@PreAuthorize("hasAnyRole('ROLE_ADMIN')")
@GetMapping(value = "/admin")
public Object admin(){
return "admin";
}
@GetMapping(value = "/admin/b")
public Object admin2(){
return "admin";
}
}
验证
Token的获取
postman直接粘贴下面请求进去即可
http://127.0.0.1:8888/oauth/token?username=user&password=123456&client_id=system&client_secret=123456&grant_type=password
正确结果为:
http请求验证
/**http 请求 URL*/
GET http://localhost:8888/user/a
/**header中需要添加消息头:*/
Authorization: Bearer 775d36ad-d224-463b-8632-9cc4e9518d41
可能出现的问题
在搭建的时候碰见比较多的问题:
- 密码的加密规则的选择,你会看到有人选择让你用明文传输(而且是官方已经不推荐的方式,那使用它的目的是什么,只是单纯的跑通,建议多思考)
- AuthenticationManager,security的认证,你要也要告诉Oauth,记得重写WebSecurityConfigurerAdapter类
- 授权,很多小伙伴都是急匆匆的把security+oauth搭建起来,发现各种问题,比如401,或者服务认证失败,如果你想看源码, 一定先看security的源码,因为你的问题8成就在这里,不要上来就直接断点到org.springframework.security.oauth2.provider.endpoint.TokenEndpoint类,你会发现根本解决不了问题(最后在啰嗦一遍,先搭建你的boot+security,在加入oauth)
- 最后就是请求的客户端,因为我们使用的内存存储方式,你在看secret的时候,让他你在此处加入的内容时什么,如果你在此处加入其它的元素,你会发现无法通过,我没有去深究源码,感兴趣的朋友自己可以去看一下
总结
小伙伴建议搭建的思路是先搭建 spring boot + spring security,保证你的鉴权是OK,spring boot 1.x版本和2.x版本是有区别的,搭建的时候自行查看文档,那么Oauth的集成速度就很快了,最后说一下。
不建议出问题首先从源码找原因,官网文档和小伙伴的博客可以作为首选,在配合跟踪源码,更容易理解,前人栽的树,你不用多浪费资源