• SpringSecurity实现用户名密码登录(Token)


      

    传统的应用是将Session放在应用服务器上,而将生成的JSESSIONID放在用户浏览器的Cookie中,而这种模式在前后端分离中就会出现以下问题

        1,开发繁琐。

        2,安全性和客户体验差

        3,有些前端技术不支持Cookie,如微信小程序

      这种情况下,前后端之间使用Token(令牌)进行通信就完美的解决上面的问题。

    ⒈添加pom依赖

     1         <dependency>
     2             <groupId>org.springframework.boot</groupId>
     3             <artifactId>spring-boot-starter-security</artifactId>
     4         </dependency>
     5         <dependency>
     6             <groupId>org.springframework.boot</groupId>
     7             <artifactId>spring-boot-starter-web</artifactId>
     8         </dependency>
     9         <dependency>
    10             <groupId>org.springframework.security.oauth</groupId>
    11             <artifactId>spring-security-oauth2</artifactId>
    12             <version>2.3.5.RELEASE</version>
    13         </dependency>
    14         <dependency>
    15             <groupId>commons-collections</groupId>
    16             <artifactId>commons-collections</artifactId>
    17             <version>3.2.2</version>
    18         </dependency>
    19         <dependency>
    20             <groupId>org.springframework.boot</groupId>
    21             <artifactId>spring-boot-starter-test</artifactId>
    22             <scope>test</scope>
    23         </dependency>
    24         <dependency>
    25             <groupId>org.springframework.security</groupId>
    26             <artifactId>spring-security-test</artifactId>
    27             <scope>test</scope>
    28         </dependency>

    ⒉编写AuthenticationSuccessHandler的实现

      1 package cn.coreqi.handler;
      2 
      3 import com.fasterxml.jackson.databind.ObjectMapper;
      4 import org.apache.commons.codec.binary.StringUtils;
      5 import org.apache.commons.collections.MapUtils;
      6 import org.slf4j.Logger;
      7 import org.slf4j.LoggerFactory;
      8 import org.springframework.beans.factory.annotation.Autowired;
      9 import org.springframework.security.authentication.BadCredentialsException;
     10 import org.springframework.security.core.Authentication;
     11 import org.springframework.security.oauth2.common.OAuth2AccessToken;
     12 import org.springframework.security.oauth2.common.exceptions.UnapprovedClientAuthenticationException;
     13 import org.springframework.security.oauth2.provider.*;
     14 import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
     15 import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
     16 import org.springframework.stereotype.Component;
     17 import javax.servlet.ServletException;
     18 import javax.servlet.http.HttpServletRequest;
     19 import javax.servlet.http.HttpServletResponse;
     20 import java.io.IOException;
     21 import java.util.Base64;
     22 
     23 @Component("coreqiAuthenticationSuccessHandler")
     24 public class CoreqiAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
     25 
     26     private Logger logger = LoggerFactory.getLogger(getClass());
     27 
     28     @Autowired
     29     private ClientDetailsService clientDetailsService;
     30 
     31     @Autowired
     32     private AuthorizationServerTokenServices authorizationServerTokenServices;
     33 
     34     @Autowired
     35     private ObjectMapper objectMapper;  //将对象转换为Json的工具类,SpringMVC在启动的时候会自动为我们注册ObjectMapper
     36 
     37     /**
     38      * @param request    不知道
     39      * @param response   不知道
     40      * @param authentication   Authentication接口是SpringSecurity的一个核心接口,它的作用是封装我们的认证信息,包含认证请求中的一些信息,包括认证请求的ip,Session是什么,以及认证用户的信息等等。
     41      * @throws IOException
     42      * @throws ServletException
     43      */
     44     @Override
     45     public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
     46         //1.从请求参数中拿到ClientId
     47         String header = request.getHeader("Authorization");
     48         if (header == null && !header.toLowerCase().startsWith("basic ")) {
     49             throw new UnapprovedClientAuthenticationException("请求头中无client信息!");
     50         }
     51         String[] tokens = this.extractAndDecodeHeader(header, request);
     52         assert tokens.length == 2;
     53 
     54         String clientId = tokens[0];
     55         String clientSecret = tokens[1];
     56 
     57         //2.通过ClientId拿到ClientDetails
     58         ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
     59         if(clientDetails == null){
     60             throw new UnapprovedClientAuthenticationException("clientId对应的配置信息不存在:" + clientId);
     61         }else if(!StringUtils.equals(clientDetails.getClientSecret(),clientSecret)){
     62             throw new UnapprovedClientAuthenticationException("clientSecret不匹配:" + clientId);
     63         }
     64         //3.创建TokenRequest
     65         TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP,clientId,clientDetails.getScope(),"custom");
     66 
     67         //4.构建OAuth2Request
     68         OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
     69 
     70         //5.构建OAuth2Authentication
     71         OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request,authentication);
     72 
     73         //6.构建OAuth2AccessToken
     74         OAuth2AccessToken token = authorizationServerTokenServices.createAccessToken(oAuth2Authentication);
     75 
     76         //7.将生成的Token返回给请求
     77         response.setContentType("application/json;charset=UTF-8");
     78         response.getWriter().write(objectMapper.writeValueAsString(token));
     79     }
     80 
     81     /**
     82      * 从请求头中解析用户名密码
     83      * @param header
     84      * @param request
     85      * @return
     86      * @throws IOException
     87      */
     88     private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {
     89         byte[] base64Token = header.substring(6).getBytes("UTF-8");
     90 
     91         byte[] decoded;
     92         try {
     93             decoded = Base64.getDecoder().decode(base64Token);
     94         } catch (IllegalArgumentException var7) {
     95             throw new BadCredentialsException("Failed to decode basic authentication token");
     96         }
     97 
     98         String token = new String(decoded, "UTF-8");
     99         int delim = token.indexOf(":");
    100         if (delim == -1) {
    101             throw new BadCredentialsException("Invalid basic authentication token");
    102         } else {
    103             return new String[]{token.substring(0, delim), token.substring(delim + 1)};
    104         }
    105     }
    106 
    107 }

    ⒊配置Security

     1 package cn.coreqi.config;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 import org.springframework.security.authentication.AuthenticationManager;
     5 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
     6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
     7 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
     8 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
     9 import org.springframework.security.crypto.password.NoOpPasswordEncoder;
    10 import org.springframework.security.crypto.password.PasswordEncoder;
    11 
    12 @EnableWebSecurity
    13 public class CoreqiWebSecurityConfig extends WebSecurityConfigurerAdapter {
    14 
    15     @Override
    16     @Bean
    17     public AuthenticationManager authenticationManagerBean() throws Exception {
    18         return super.authenticationManagerBean();
    19     }
    20 
    21     @Override
    22     protected void configure(HttpSecurity http) throws Exception {
    23         http.httpBasic()
    24                 .and()
    25                 .authorizeRequests()
    26                 .antMatchers("/oauth/token","/login").permitAll()
    27                 .anyRequest().authenticated()  //任何请求都需要身份认证
    28                 .and().csrf().disable();    //禁用CSRF
    29     }
    30 
    31     @Override
    32     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    33         auth.inMemoryAuthentication()
    34                 .withUser("fanqi").password("admin").roles("admin");
    35     }
    36 
    37     @Bean
    38     public PasswordEncoder passwordEncoder()
    39     {
    40         return NoOpPasswordEncoder.getInstance();
    41     }
    42 }

    ⒋配置OAuth2

     1 package cn.coreqi.config;
     2 
     3 import org.springframework.beans.factory.annotation.Autowired;
     4 import org.springframework.beans.factory.annotation.Qualifier;
     5 import org.springframework.context.annotation.Configuration;
     6 import org.springframework.security.authentication.AuthenticationManager;
     7 import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
     8 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
     9 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
    10 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
    11 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
    12 
    13 @Configuration
    14 @EnableAuthorizationServer  //开启认证服务器
    15 public class CoreqiAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    16 
    17     @Autowired
    18     @Qualifier("authenticationManagerBean")
    19     private AuthenticationManager authenticationManager;
    20 
    21     @Autowired
    22     private AuthenticationConfiguration authenticationConfiguration;
    23 
    24     /**
    25      * password模式需要提供一个AuthenticationManager到AuthorizationServerEndpointsConfigurer
    26      * @param authorizationServerEndpointsConfigurer
    27      * @throws Exception
    28      */
    29     @Override
    30     public void configure(AuthorizationServerEndpointsConfigurer authorizationServerEndpointsConfigurer) throws Exception {
    31         authorizationServerEndpointsConfigurer.authenticationManager(authenticationConfiguration.getAuthenticationManager());
    32     }
    33 
    34     @Override
    35     public void configure(ClientDetailsServiceConfigurer clientDetailsServiceConfigurer) throws Exception {
    36         clientDetailsServiceConfigurer.inMemory()
    37                 .withClient("coreqi")
    38                 .secret("coreqiSecret")
    39                 .redirectUris("https://www.baidu.com")
    40                 .scopes("ALL")
    41                 .authorities("COREQI_READ")
    42                 .authorizedGrantTypes("authorization_code","password");
    43     }
    44 
    45 }

    ⒌配置资源服务器

     1 package cn.coreqi.config;
     2 
     3 import cn.coreqi.handler.CoreqiAuthenticationSuccessHandler;
     4 import org.springframework.beans.factory.annotation.Autowired;
     5 import org.springframework.context.annotation.Configuration;
     6 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
     7 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
     8 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
     9 import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
    10 
    11 @Configuration
    12 @EnableResourceServer   //开启资源服务器
    13 public class CoreqiResourceServerConfig extends ResourceServerConfigurerAdapter {
    14 
    15     @Autowired
    16     private CoreqiAuthenticationSuccessHandler coreqiAuthenticationSuccessHandler;
    17 
    18     @Override
    19     public void configure(HttpSecurity http) throws Exception {
    20         http.formLogin()
    21                 .successHandler(coreqiAuthenticationSuccessHandler)
    22             .and()
    23             .authorizeRequests()
    24                 .antMatchers("/oauth/token","/login").permitAll()
    25                 .anyRequest().authenticated()  //任何请求都需要身份认证
    26             .and()
    27             .csrf()
    28                 .disable();    //禁用CSRF
    29     }
    30 
    31 }

    ⒍测试

        

  • 相关阅读:
    Redis-Sentinel 哨兵
    virtualenv and virtualenvwrapper
    C/C++中extern关键字详解
    C++ 中文拼音排序方法。
    vector排序
    VS2013 Ctrl+Shift+F 没反应
    PostMessage 解析
    CTextUI 文本控件 显示数字方法
    SetTimer API函数
    CEditUI 控件使用
  • 原文地址:https://www.cnblogs.com/fanqisoft/p/10665144.html
Copyright © 2020-2023  润新知