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 }

⒍测试

    

原文地址:https://www.cnblogs.com/fanqisoft/p/10665144.html