Spring Boot整合oauth2.0搭建統一授權服務(密碼模式)

前言

寫這個博客得原因是最近想要將自己得api單獨發佈給第三方使用,但是又不想被別人濫用,所以想弄一個授權服務,但是網上關於oauth2.0的資料層出不窮,看了之後完全不明白應該如果實際的去整合,現在基本成功後記錄下來。
關於oauth2.0的概念以及相關的知識等可以建議參閱理解OAuth 2.0

準備

新建一個Spring Boot的web項目並導入一下依賴:
<dependency>
	<groupId>org.springframework.security.oauth</groupId>
	<artifactId>spring-security-oauth2</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-web</artifactId>
</dependency>

配置

配置EnableAuthorizationServer
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationConfig extends AuthorizationServerConfigurerAdapter {


    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userService;

    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("test")//客戶端ID
                .authorizedGrantTypes("password", "refresh_token")//設置驗證方式
                .scopes("read", "write")
                .secret("123456")
                .accessTokenValiditySeconds(10000) //token過期時間
                .refreshTokenValiditySeconds(10000); //refresh過期時間
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                .authenticationManager(authenticationManager)
                .userDetailsService(userService); //配置userService 這樣每次認證的時候會去檢驗用戶是否鎖定,有效等
    }

    @Bean
    public TokenStore tokenStore() {
        //使用內存的tokenStore
        return new InMemoryTokenStore();
    }
}
UserService接口定義如下:
public interface UserService extends UserDetailsService {
    //後期在此新增UserService的業務接口
}
其中UserDetailsService只包含一個需要實現的方法,具體實現:
@Primary
@Service("userService")
public class UserServiceImpl implements UserService {

    private final static Set<User> users = new HashSet<>();

    static {
        users.add(new User(1, "test-user1", "123451"));
        users.add(new User(2, "test-user2", "123452"));
        users.add(new User(3, "test-user3", "123453"));
        users.add(new User(4, "test-user4", "123454"));
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Optional<User> user = users.stream()
                .filter((u) -> u.getUserName().equals(s))
                .findFirst();
        if (!user.isPresent())
            throw new UsernameNotFoundException("there's no user founded!");
        else
            return UserDetailConverter.convert(user.get());
    }

    private static class UserDetailConverter {
        static UserDetails convert(User user) {
            return new MyUserDetails(user);
        }
    }
}
根據方法名很容易明白spring 的組件會調用此方法去獲取到用戶的信息並去驗證。
這裏的用戶信息直接寫死的,實際中可以用jdbc或者其他配置等方式。
同時loadUserByUsername方法要求返回一個UserDetails接口:
public interface UserDetails extends Serializable {
    Collection<? extends GrantedAuthority> getAuthorities();

    String getPassword();

    String getUsername();

    boolean isAccountNonExpired();

    boolean isAccountNonLocked();

    boolean isCredentialsNonExpired();

    boolean isEnabled();
}
並且spring已經有了它的一個實現:org.springframework.security.core.userdetails.User類
由於User這個名字很容易和我們項目中的實體User重名這裏選擇繼承這個類自己實現一個UserDetails:
/**
 * 自定義UserDetails類 攜帶User實例
 */
public class MyUserDetails extends User {

    private com.example.oauth.pojo.User user;

    public MyUserDetails(com.example.oauth.pojo.User user) {
        super(user.getUserName(), user.getPassword(), true, true, true, true, Collections.EMPTY_SET);
        this.user = user;
    }

    public com.example.oauth.pojo.User getUser() {
        return user;
    }

    public void setUser(com.example.oauth.pojo.User user) {
        this.user = user;
    }
}
到此基本的配置就已經完成了,

測試

寫一個測試的Controller:

@RestController
public class UserController {

    @Autowired
    private TokenStore tokenStore;

    @PostMapping("/bar")
    public String bar(@RequestHeader("Authorization") String auth) {

        MyUserDetails userDetails = (MyUserDetails) tokenStore.readAuthentication(auth.split(" ")[1]).getPrincipal();

        User user = userDetails.getUser();

        return user.getUserName() + ":" + user.getPassword();
    }
}
啓動項目:
訪問:localhost:8080/bar
返回未授權

訪問:localhost:8080/oauth/token獲取token

這裏的Authrization字段中Basic後面是配置中的clientId + ":" + secret的Base64編碼結果

我這裏是:test:123456的Base64編碼結果

返回token信息:

使用token訪問controller:

成功。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章