Spring Security(十二):认证(OAuth2)

您所在的位置:网站首页 信而富登陆用户名 Spring Security(十二):认证(OAuth2)

Spring Security(十二):认证(OAuth2)

2024-05-30 21:17| 来源: 网络整理| 查看: 265

上文讲了授权码模式,了解授权码模式可以很好的了解授权模式,但实际开发中也需要 "用户名+密码"方式来获取授权,本文将"用户名密码"这种方式接入到标准的OAuth2流程中,登录成功后返回token,根据token访问其它接口。

1. pom.xml 4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.5.RELEASE com.example springboot-security-example 0.0.1-SNAPSHOT springboot-security-example Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure 2.1.2.RELEASE org.springframework.security.oauth spring-security-oauth2 2.3.4.RELEASE compile org.projectlombok lombok org.springframework.boot spring-boot-starter-test org.springframework.boot spring-boot-maven-plugin 2. application.yml server: port: 8080 logging: level: org.springframework: debug 3. SecurityConfiguration @Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Autowired private MyUserDetailsService myUserDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/authentication/form") .successHandler(myAuthenticationSuccessHandler) .failureHandler(myAuthenticationFailureHandler); http.authorizeRequests() .antMatchers("/login", "/authentication/form").permitAll() .anyRequest().authenticated() .and().csrf().disable(); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * 对原始字符不进行加密,比较时都会返回true */ // @Bean // public PasswordEncoder passwordEncoder() { // return new PasswordEncoder() { // @Override // public String encode (CharSequence charSequence) { // return charSequence.toString(); // } // @Override // public boolean matches(CharSequence charSequence, String s) { // return true; // } // }; // } } 4. UserDetailsService @Component public class MyUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("admin")); } public static void main(String[] args) { System.out.println(new BCryptPasswordEncoder().encode("123456")); } } 5. AuthenticationSuccessHandler @Slf4j @Component public class MyAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { @Autowired private ObjectMapper objectMapper; @Autowired private ClientDetailsService clientDetailsService; @Autowired private AuthorizationServerTokenServices authorizationServerTokenServices; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { log.info("login sucesssful {}", objectMapper.writeValueAsString(authentication)); String header = request.getHeader("Authorization"); if (header == null || !header.toLowerCase().startsWith("basic ")) { throw new UnapprovedClientAuthenticationException("请求头中没有clientId"); } String[] tokens = extractAndDecodeHeader(header, request); assert tokens.length == 2; String clientId = tokens[0]; String clientSecret = tokens[1]; ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId); if (clientDetails == null) { throw new UnapprovedClientAuthenticationException("clientId配置信息不存在,clientId=" + clientId); } else if (!new BCryptPasswordEncoder().matches(clientSecret, clientDetails.getClientSecret())) { throw new UnapprovedClientAuthenticationException("clientSecret不匹配,clientId=" + clientId); } // grantType 为自定义的"custom" TokenRequest tokenRequest = new TokenRequest(new HashMap(), clientId, clientDetails.getScope(), "custom"); OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails); OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request, authentication); OAuth2AccessToken accessToken = authorizationServerTokenServices.createAccessToken(oAuth2Authentication); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(objectMapper.writeValueAsString(accessToken)); } /** * Decodes the header into a username and password. * * @throws BadCredentialsException if the Basic header is not present or is not valid * Base64 */ private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException { byte[] base64Token = header.substring(6).getBytes("UTF-8"); byte[] decoded; try { decoded = Base64.getDecoder().decode(base64Token); } catch (IllegalArgumentException e) { throw new BadCredentialsException( "Failed to decode basic authentication token"); } String token = new String(decoded, "UTF-8"); int delim = token.indexOf(":"); if (delim == -1) { throw new BadCredentialsException("Invalid basic authentication token"); } return new String[] { token.substring(0, delim), token.substring(delim + 1) }; } } 6. AuthenticationFailureHandler @Slf4j @Component public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException { log.info("认证失败"); response.setContentType("text/html;charset=utf-8"); response.getWriter().write(exception.getMessage()); } } 7. AuthorizationServerConfiguration @Configuration @EnableAuthorizationServer public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() // 使用in-memory存储 .withClient("clientId") .secret(new BCryptPasswordEncoder().encode("clientSecret")) .authorizedGrantTypes("authorization_code") .scopes("all") .redirectUris("http://www.baidu.com"); } @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security .tokenKeyAccess("permitAll()") .checkTokenAccess("permitAll()") .allowFormAuthenticationForClients(); } } 8. ResourceServerConfiguration 资源服务器@EnableResourceServer用于保护接口,验证token,如果token有效就可以访问api认证服务器@EnableAuthorizationServer用于获取token资源服务器可以单独配置在要保护的项目上,也可以与认证服务器配置在同一个项目中 @Configuration @EnableResourceServer public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Autowired private MyAuthenticationFailureHandler myAuthenticationFailureHandler; @Override public void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/authentication/form") .successHandler(myAuthenticationSuccessHandler) .failureHandler(myAuthenticationFailureHandler); http.authorizeRequests() .antMatchers("/login", "/authentication/form").permitAll() .anyRequest().authenticated() .and().csrf().disable(); } } 9. UserController @RestController public class UserController { @Autowired private ObjectMapper objectMapper; @GetMapping(value = "/user/me", produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public String me() throws JsonProcessingException { Authentication auth = SecurityContextHolder.getContext().getAuthentication(); Object principal = auth.getPrincipal(); return objectMapper.writeValueAsString(principal); } } 10. 获取token

访问登录地址"authentication/form"获取token, 这里使用Chome插件 rest api客户端来代替登录页面来发送登录请求。

登录地址在http.formLogin().loginPage(“authentication/form”)中配置username和password要和UserDetailsService中的值保持一致 在这里插入图片描述 Authorization中的Username和Password为AuthorizationServerConfiguration配置中的client和secret值。clients.inMemory().withClient("clientId").secret(new BCryptPasswordEncoder().encode("clientSecret")) 在这里插入图片描述 11. 访问资源接口

访问UserController中的接口"/user/me", 请求头中需要添加Authorization头,值为 "bearer " + access_token值,注意bearer字符串和access_token中间有个空格,access_token为上面获取token的的“access_token”字段值 在这里插入图片描述

在这里插入图片描述



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3