SpringBoot--实战开发--OAuth2.0密码模式(五十一)

一、密码模式(User Credentials)简介

密码模式(resource owner password credentials)(为遗留系统设计)(支持refresh token)
授权码模式(authorization code)(正宗方式)(支持refresh token)
简化模式(implicit)(为web浏览器应用设计)(不支持refresh token)
客户端模式(client credentials)(为后台api服务消费者设计)(不支持refresh token)

完整的项目结构分为:客户端服务,认证服务,资源服务。客户端需要访问资源服务的资源时,则需要得到认证服务的认证。
  密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。
这种模式是最不推荐的,因为client可能存了用户密码。
这种模式主要用来做遗留项目升级为oauth2的适配方案。
  在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下,比如客户端是操作系统的一部分,或者由一个著名公司出品。而认证服务器只有在其他授权模式无法执行的情况下,才能考虑使用这种模式。

(A)用户向客户端提供用户名和密码
(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。
客户端发出的HTTP请求,包含以下参数:
grant_type:表示授权类型,此处的值固定为"password",必选项。
username:表示用户名,必选项。
password:表示用户的密码,必选项。
scope:表示权限范围,可选项。

   POST /token HTTP/1.1
     Host: server.example.com
     Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
     Content-Type: application/x-www-form-urlencoded
     grant_type=password&username=johndoe&password=A3ddj3w

(C)认证服务器确认无误后,向客户端提供访问令牌。
认证服务器向客户端发送访问令牌

  HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache
     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }
密码模式

二、Maven依赖


    org.springframework.cloud
    spring-cloud-starter-oauth2

三、代码实现

  1. 实体类
    登录用户名密码,
@Data
@AllArgsConstructor
public class User {
    private long userId;
    private String userName;
    private String password;
}

将SpringUser类与自定义类进行转换:

import org.springframework.security.core.userdetails.User;
/**
 * 自定义UserDetails类 携带User实例
 */
@Data
public class MyUserDetails extends User {
    private com.xtsz.springbootauth2.entity.User user;
    public MyUserDetails(com.xtsz.springbootauth2.entity.User user) {
        super(user.getUserName(), user.getPassword(), true, true, true, true, Collections.EMPTY_SET);
        this.user = user;
    }
}
  1. 用户接口
    继承Spring UserDetailsService 接口:
import org.springframework.security.core.userdetails.UserDetailsService;
public interface UserService extends UserDetailsService {
}

实现类:

@Primary
@Service("userService")
public class UserServiceImpl implements UserService {
    private final static Set users = new HashSet<>();

    static {
        users.add(new User(1, "admin", new BCryptPasswordEncoder().encode("123456")));
    }

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        Optional user = users.stream()
                .filter((u) -> u.getUserName().equals(s))
                .findFirst();
        if (!user.isPresent())
            throw new UsernameNotFoundException("用户不能发现!");
        else
            return UserDetailConverter.convert(user.get());
    }

    private static class UserDetailConverter {
        static UserDetails convert(User user) {
            return new MyUserDetails(user);
        }
    }
}
  1. Security配置
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * Spring Boot 2 配置,这里要bean 注入
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        AuthenticationManager manager = super.authenticationManagerBean();
        return manager;
    }

    /**
     * 加密方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
  1. 认证服务配置
/**
 * 认证服务配置
 */
@Configuration
@EnableAuthorizationServer
@Slf4j
public class AuthorizationServerConfiguration 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("client") // 客户端ID
                .scopes("all")
                .authorizedGrantTypes("password", "refresh_token") // 设置验证方式
                .secret(new BCryptPasswordEncoder().encode("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();
    }
}

  1. 资源服务配置
/**
 * 资源服务配置
 */
@Configuration
@EnableResourceServer()
public class ResourceServerConfiguration  extends ResourceServerConfigurerAdapter {
       @Override
    public void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.authorizeRequests()
                .antMatchers(HttpMethod.OPTIONS).permitAll()
                .antMatchers("/account/**").authenticated()
                .and().httpBasic()
                .and().csrf().disable();
    }
}

  1. 控制器
@RestController
@RequestMapping("/account")
@Slf4j
public class UserController {
    @GetMapping("user")
    public String getServer(){
            return "9999服务器";
    }
}

四、测试

  1. 访问:http://localhost:8080/account/user
    无权限
  2. 获取令牌


    获取令牌

    获取令牌

    获取令牌

    获取令牌

    获取成功

五、常见问题:

1、Caused by: java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
原因:JDK大于1.8。

        
            javax.xml.bind
            jaxb-api
            2.3.0
        
        
            com.sun.xml.bind
            jaxb-impl
            2.3.0
        
        
            com.sun.xml.bind
            jaxb-core
            2.3.0
        
        
            javax.activation
            activation
            1.1.1
        

你可能感兴趣的:(SpringBoot--实战开发--OAuth2.0密码模式(五十一))