密码模式示例

  • 密码模式,需要用户直接在第三方应用上输入用户名密码登录

  • 接下来的代码是在授权码模式的基础上改造

  1. 首先对 auth-server 进行改造,使之支持 password 模式
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("javaboy")
            .secret(new BCryptPasswordEncoder().encode("123"))
            .resourceIds("res1")
            .authorizedGrantTypes("password","refresh_token")
            .scopes("all")
            .redirectUris("http://localhost:8082/index.html");
}

这里其他地方都不变,主要是在 authorizedGrantTypes 中增加了 password 模式。

由于使用了 password 模式之后,用户要进行登录,所以我们需要配置一个 <认证服务管理器> AuthenticationManager,还是在 AuthorizationServer 类中,具体配置如下:

@Autowired
AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints
            .authenticationManager(authenticationManager)
            .tokenServices(tokenServices());
}
  • 注意,在授权码模式中,我们配置的 AuthorizationCodeServices 现在不需要了,取而代之的是 authenticationManager。

那么这个 authenticationManager 实例从哪里来呢?这需要我们在 Spring Security 的配置中提供,在 SecurityConfig 中添加如下代码:

@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
}
  • 配置完成后,重启 auth-server。

配置 client-app_8082

  1. 首先我们添加登录功能,修改 index.html ,如下:
<body>
你好,osvue!

<form action="/login" method="post">
    <table>
        <tr>
            <td>用户名:</td>
            <td><input name="username"></td>
        </tr>
        <tr>
            <td>密码:</td>
            <td><input name="password"></td>
        </tr>
        <tr>
            <td><input type="submit" value="登录"></td>
        </tr>
    </table>
</form>
<h1 th:text="${msg}"></h1>
</body>

这一个简单的登录功能。

  • 登录接口:

/**
 * @Author: Mr.Han
 * @Description: spring_boot_plus
 * @Date: Created in 2020/4/17_11:00
 * @Modified By: THE GIFTED
 */
@Controller
public class HelloController {
  @Autowired
  RestTemplate restTemplate;
  @PostMapping("/login")
  public String login(String username, String password,Model model) {
    MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
    map.add("username", username);
    map.add("password", password);
    map.add("client_secret", "123");
    map.add("client_id", "osvue");
    map.add("grant_type", "password");
//    请求认证服务器, 获取token
    Map<String,String> resp = restTemplate.postForObject("http://localhost:8080/oauth/token", map, Map.class);
    String access_token = resp.get("access_token");
    HttpHeaders headers = new HttpHeaders();
//    携带token , 请求资源服务器
    headers.add("Authorization", "Bearer " + access_token);
    HttpEntity<Object> httpEntity = new HttpEntity<>(headers);
    ResponseEntity<String> entity = restTemplate.exchange("http://localhost:8081/admin/hello", HttpMethod.GET, httpEntity, String.class);
    model.addAttribute("msg", entity.getBody());
    return "index";
  }
}

在登录接口中,当收到一个用户名密码之后,我们通过 RestTemplate 发送一个 POST 请求,注意 post 请求中,grant_type 参数的值为 password,通过这个请求,我们可以获取 auth-server 返回的 access_token,格式如下:

{
    "access_token": "d944238e-3e45-4756-aba1-4a5bf4fc1a10",
    "token_type": "bearer",
    "refresh_token": "c0b67e30-424a-4fea-b2a3-71cdffe15966",
    "expires_in": 7199,
    "scope": "all"
}

我们提取出 access_token 之后,接下来去请求资源服务器,并将访问到的数据放在 model 中。

OK,配置完成后,启动 client-app,访问 http://localhost:8082/index.html 页面进行测试。