springboot+spring security +oauth2.0 demo搭建(password模式)(認證受權端與資源服務端分離的形式)

項目security_simple(認證受權項目)

1.新建springboot項目

 

 

這兒選擇springboot版本我選擇的是2.0.6前端

點擊finish後完成項目的建立

2.引入maven依賴  下面是我引入的依賴

  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  4     <modelVersion>4.0.0</modelVersion>
  5 
  6     <groupId>com.megalith</groupId>
  7     <artifactId>security_simple</artifactId>
  8     <version>0.0.1-SNAPSHOT</version>
  9     <packaging>jar</packaging>
 10 
 11     <name>security_simple</name>
 12     <description>Demo project for Spring Boot</description>
 13 
 14     <parent>
 15         <groupId>org.springframework.boot</groupId>
 16         <artifactId>spring-boot-starter-parent</artifactId>
 17         <version>2.0.6.RELEASE</version>
 18         <relativePath/> <!-- lookup parent from repository -->
 19     </parent>
 20 
 21     <properties>
 22         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 23         <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 24         <java.version>1.8</java.version>
 25         <pagehelper.version>1.1.0</pagehelper.version>
 26         <mybatis.generator.version>1.3.2</mybatis.generator.version>
 27         <fastjson.version>1.2.31</fastjson.version>
 28         <jackson.version>2.9.7</jackson.version>
 29     </properties>
 30 
 31     <dependencies>
 32         <dependency>
 33             <groupId>junit</groupId>
 34             <artifactId>junit</artifactId>
 35             <version>4.11</version>
 36             <scope>test</scope>
 37         </dependency>
 38 
 39         <dependency>
 40             <groupId>org.springframework.boot</groupId>
 41             <artifactId>spring-boot-starter-web</artifactId>
 42             <exclusions>
 43                 <exclusion>
 44                     <groupId>org.springframework.boot</groupId>
 45                     <artifactId>spring-boot-starter-tomcat</artifactId>
 46                 </exclusion>
 47             </exclusions>
 48         </dependency>
 49         <dependency>
 50             <groupId>org.springframework.boot</groupId>
 51             <artifactId>spring-boot-configuration-processor</artifactId>
 52             <optional>true</optional>
 53         </dependency>
 54         <dependency>
 55             <groupId>org.springframework.boot</groupId>
 56             <artifactId>spring-boot-starter-jetty</artifactId>
 57         </dependency>
 58 
 59 
 60         <!-- aop -->
 61         <dependency>
 62             <groupId>org.springframework.boot</groupId>
 63             <artifactId>spring-boot-starter-aop</artifactId>
 64         </dependency>
 65         <!-- json處理 -->
 66         <dependency>
 67             <groupId>com.alibaba</groupId>
 68             <artifactId>fastjson</artifactId>
 69             <version>${fastjson.version}</version>
 70         </dependency>
 71 
 72         <!-- jackson json begin -->
 73         <dependency>
 74             <groupId>com.fasterxml.jackson.core</groupId>
 75             <artifactId>jackson-core</artifactId>
 76             <version>${jackson.version}</version>
 77         </dependency>
 78         <dependency>
 79             <groupId>com.fasterxml.jackson.core</groupId>
 80             <artifactId>jackson-databind</artifactId>
 81             <version>${jackson.version}</version>
 82         </dependency>
 83         <dependency>
 84             <groupId>com.fasterxml.jackson.core</groupId>
 85             <artifactId>jackson-annotations</artifactId>
 86             <version>${jackson.version}</version>
 87         </dependency>
 88         <dependency>
 89             <groupId>com.fasterxml.jackson.module</groupId>
 90             <artifactId>jackson-module-jaxb-annotations</artifactId>
 91             <version>${jackson.version}</version>
 92         </dependency>
 93         <!-- jackson json end -->
 94         <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
 95         <dependency>
 96             <groupId>org.apache.commons</groupId>
 97             <artifactId>commons-lang3</artifactId>
 98             <version>3.3.2</version>
 99         </dependency>
100         <dependency>
101             <groupId>com.fasterxml.uuid</groupId>
102             <artifactId>java-uuid-generator</artifactId>
103             <version>3.1.3</version>
104         </dependency>
105 
106 
107         <!-- druid datasource -->
108         <dependency>
109             <groupId>com.alibaba</groupId>
110             <artifactId>druid</artifactId>
111             <version>1.0.27</version>
112         </dependency>
113         <!-- mybatis -->
114         <dependency>
115             <groupId>org.mybatis.spring.boot</groupId>
116             <artifactId>mybatis-spring-boot-starter</artifactId>
117             <version>1.3.0</version>
118         </dependency>
119         <!--mybatis代碼生成器 -->
120         <dependency>
121             <groupId>org.mybatis.generator</groupId>
122             <artifactId>mybatis-generator-core</artifactId>
123             <version>${mybatis.generator.version}</version>
124         </dependency>
125         <!-- bean驗證 -->
126         <dependency>
127             <groupId>org.springframework.boot</groupId>
128             <artifactId>spring-boot-starter-validation</artifactId>
129         </dependency>
130 
131         <!-- mysql驅動 -->
132         <dependency>
133             <groupId>mysql</groupId>
134             <artifactId>mysql-connector-java</artifactId>
135             <scope>runtime</scope>
136         </dependency>
137         <!-- spring boot 開發工具 -->
138         <dependency>
139             <groupId>org.springframework.boot</groupId>
140             <artifactId>spring-boot-devtools</artifactId>
141             <optional>true</optional>
142         </dependency>
143         <!-- 測試 -->
144         <dependency>
145             <groupId>org.springframework.boot</groupId>
146             <artifactId>spring-boot-starter-test</artifactId>
147             <scope>test</scope>
148         </dependency>
149 
150 
151         <dependency>
152             <groupId>org.springframework.boot</groupId>
153             <artifactId>spring-boot-starter-data-jpa</artifactId>
154         </dependency>
155 
156         <!--spring-security-oauth2 begin-->
157         <dependency>
158             <groupId>org.springframework.boot</groupId>
159             <artifactId>spring-boot-starter-security</artifactId>
160         </dependency>
161         <dependency>
162             <groupId>org.springframework.security.oauth.boot</groupId>
163             <artifactId>spring-security-oauth2-autoconfigure</artifactId>
164             <version>2.0.6.RELEASE</version>
165         </dependency>
166         <!--spring-security-oauth2 end-->
167     </dependencies>
168 
169     <build>
170         <plugins>
171             <plugin>
172                 <groupId>org.springframework.boot</groupId>
173                 <artifactId>spring-boot-maven-plugin</artifactId>
174             </plugin>
175         </plugins>
176     </build>
177 
178 
179 </project>

3.新建數據庫(由於本項目採用jdbc的形式存儲token相關)

  建表sql語句的地址爲    https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql  這兒記得不一樣的數據庫中的特殊字段須要修改一下,建好的表以下java

 

4.進行配置

  4.1 CustomAuthorizationServerConfig 類

  

  1 import org.springframework.beans.factory.annotation.Autowired;
  2 import org.springframework.context.annotation.Bean;
  3 import org.springframework.context.annotation.Configuration;
  4 import org.springframework.http.HttpHeaders;
  5 import org.springframework.security.authentication.AuthenticationManager;
  6 import org.springframework.security.core.userdetails.UserDetailsService;
  7 import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
  8 import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
  9 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
 10 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
 11 import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
 12 import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
 13 import org.springframework.security.oauth2.provider.token.TokenStore;
 14 import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
 15 import org.springframework.web.cors.CorsConfiguration;
 16 import org.springframework.web.cors.CorsConfigurationSource;
 17 import org.springframework.web.filter.CorsFilter;
 18 
 19 import javax.annotation.Resource;
 20 import javax.servlet.http.HttpServletRequest;
 21 import javax.sql.DataSource;
 22 
 23 /**
 24  * @Description: 配置受權認證服務類
 25  * @author: zhoum
 26  * @Date: 2018-11-22
 27  * @Time: 13:41
 28  */
 29 @Configuration
 30 @EnableAuthorizationServer //受權認證中心
 31 public class CustomAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
 32 
 33     /**
 34      * 權限驗證控制器
 35      */
 36     @Autowired
 37     private AuthenticationManager authenticationManager;
 38     /**
 39      * 數據源,保存token的時候須要 默認爲spring中配置的datasource
 40      */
 41     @Autowired
 42     private DataSource dataSource;
 43     /**
 44      * 設置保存token的方式,一共有五種,這裏採用數據庫的方式
 45      */
 46     @Autowired
 47     private TokenStore tokenStore;
 48 
 49     @Bean
 50     public TokenStore tokenStore() {
 51         return new JdbcTokenStore(dataSource);
 52     }
 53 
 54     /**
 55      * 自定義登陸或者鑑權失敗時的返回信息
 56      */
 57     @Resource(name = "webResponseExceptionTranslator")
 58     private WebResponseExceptionTranslator webResponseExceptionTranslator;
 59     
 60     
 61     /******************配置區域**********************/
 62 
 63 
 64     /**
 65      * 用來配置受權(authorizatio)以及令牌(token)的訪問端點和令牌服務   核心配置  在啓動時就會進行配置
 66      */
 67     @Override
 68     public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
 69         //開啓密碼受權類型
 70         endpoints.authenticationManager(authenticationManager);
 71         //配置token存儲方式
 72         endpoints.tokenStore(tokenStore);
 73         //自定義登陸或者鑑權失敗時的返回信息
 74         endpoints.exceptionTranslator(webResponseExceptionTranslator);
 75     }
 76 
 77     /**
 78      * 用來配置令牌端點(Token Endpoint)的安全約束.
 79      */
 80     @Override
 81     public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
 82         /**
 83          * 配置oauth2服務跨域
 84          */
 85         CorsConfigurationSource source = new CorsConfigurationSource() {
 86             @Override
 87             public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
 88                 CorsConfiguration corsConfiguration = new CorsConfiguration();
 89                 corsConfiguration.addAllowedHeader("*");
 90                 corsConfiguration.addAllowedOrigin(request.getHeader(HttpHeaders.ORIGIN));
 91                 corsConfiguration.addAllowedMethod("*");
 92                 corsConfiguration.setAllowCredentials(true);
 93                 corsConfiguration.setMaxAge(3600L);
 94                 return corsConfiguration;
 95             }
 96         };
 97 
 98         security.tokenKeyAccess("permitAll()")
 99                 .checkTokenAccess("permitAll()")
100                 .allowFormAuthenticationForClients()
101                 .addTokenEndpointAuthenticationFilter(new CorsFilter(source));
102     }
103 
104     /**
105      * 用來配置客戶端詳情服務(ClientDetailsService),
106      * 客戶端詳情信息在這裏進行初始化,  數據庫在進行client_id 與 client_secret驗證時   會使用這個service進行驗證
107      */
108     @Override
109     public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
110         clients.jdbc(dataSource);
111     }
112 }

  注意: 1. authenticationManager 的配置類會在後面的WebSecurityAdaptConfig配置中給出mysql

      2.TokenStore 一共有五種配置方式  本項目採用jdbc也就是數據庫的形式git

      

      3.WebResponseExceptionTranslator爲自定義的驗證錯誤異常返回類,代碼以下,其中responsedata爲自定義的數據返回類 包含code,message,data三個屬性github

        

 1 import cn.com.megalith.common.ResponseData;
 2 import org.springframework.context.annotation.Bean;
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.http.HttpHeaders;
 5 import org.springframework.http.HttpStatus;
 6 import org.springframework.http.ResponseEntity;
 7 import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
 8 import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
 9 import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
10 
11 /**
12  * @Description: web全局異常返回處理器
13  * @author: zhoum
14  * @Date: 2018-11-22
15  * @Time: 14:48
16  */
17 @Configuration
18 public class WebResponseExceptionTranslateConfig {
19     /**
20      * 自定義登陸或者鑑權失敗時的返回信息
21      */
22     @Bean(name = "webResponseExceptionTranslator")
23     public WebResponseExceptionTranslator webResponseExceptionTranslator() {
24         return new DefaultWebResponseExceptionTranslator() {
25             @Override
26             public ResponseEntity translate(Exception e) throws Exception {
27                 ResponseEntity responseEntity = super.translate(e);
28                 OAuth2Exception body = (OAuth2Exception) responseEntity.getBody();
29                 HttpHeaders headers = new HttpHeaders();
30                 headers.setAll(responseEntity.getHeaders().toSingleValueMap());
31                 // do something with header or response
32                 if ( 400 == responseEntity.getStatusCode().value() ) {
33                     System.out.println(body.getMessage());
34                     if ( "Bad credentials".equals(body.getMessage()) ) {
35                         return new ResponseEntity(new ResponseData("400" , "您輸入的用戶名或密碼錯誤" , null) , headers , HttpStatus.OK);
36                     }
37                 }
38                 return new ResponseEntity(body , headers , responseEntity.getStatusCode());
39 
40             }
41         };
42     }
43 }

 

  4.2ResourceServerConfig

  

 1 import org.springframework.context.annotation.Configuration;
 2 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 3 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
 4 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 5 
 6 /**
 7  * @Description: 資源提供端的配置  
 8  * @author: zhoum
 9  * @Date: 2018-11-22
10  * @Time: 16:58
11  */
12 @Configuration
13 @EnableResourceServer //開啓資源提供服務的配置  是默認狀況下spring security oauth2的http配置   會被WebSecurityConfigurerAdapter的配置覆蓋
14 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
15 
16     @Override
17     public void configure(HttpSecurity http) throws Exception {
18         http
19                 .authorizeRequests()
20                 .antMatchers("/**").permitAll();
21     }
22 }

  這兒注意,後面的WebSecurityAdaptConfig的配置類會覆蓋這兒的配置web

  4.3 WebSecurityAdaptConfig  配置類

  

 1 import cn.com.megalith.auth.CustomerAuthenticationProvider;
 2 import org.springframework.beans.factory.annotation.Autowired;
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.security.authentication.AuthenticationManager;
 6 import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
 7 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 8 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 9 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
10 import org.springframework.security.core.userdetails.UserDetailsService;
11 import org.springframework.security.crypto.password.PasswordEncoder;
12 
13 import javax.annotation.Resource;
14 
15 /**
16  * @Description: //是默認狀況下spring security的http配置 優於ResourceServerConfigurerAdapter的配置
17  * @author: zhoum
18  * @Date: 2018-11-22
19  * @Time: 17:06
20  */
21 @Configuration              //開啓三種能夠在方法上面加權限控制的註解
22 @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
23 public class WebSecurityAdaptConfig extends WebSecurityConfigurerAdapter {
24 
25     /**
26      * 獲取用戶的驗證配置類
27      */
28     @Resource(name = "signUserDetailService")
29     private UserDetailsService userDetailsService;
30     /**
31      * 加密配置
32      */
33     @Autowired
34     private PasswordEncoder passwordEncoder;
35 
36     /**
37      * 密碼驗證處理器
38      */
39     @Resource(name = "myCustomerAuthenticationProvider")
40     private CustomerAuthenticationProvider customerAuthenticationProvider;
41 
42     /**
43      * spring security設置
44      */
45     @Override
46     protected void configure(HttpSecurity http) throws Exception {
47         http
48                 .authorizeRequests()//定義哪些url須要被保護  哪些不須要保護
49                 .antMatchers("/oauth/token" , "oauth/check_token").permitAll()//定義這兩個連接不須要登陸可訪問
50                 .antMatchers("/**").permitAll() //定義全部的都不須要登陸  目前是測試須要
51                 .anyRequest().authenticated() //其餘的都須要登陸
52                 //.antMatchers("/sys/**").hasRole("admin")///sys/**下的請求 須要有admin的角色
53                 .and()
54                 .formLogin().loginPage("/login")//若是未登陸則跳轉登陸的頁面   這兒能夠控制登陸成功和登陸失敗跳轉的頁面
55                 .usernameParameter("username").passwordParameter("password").permitAll()//定義號碼與密碼的parameter
56                 .and()
57                 .csrf().disable();//防止跨站請求  spring security中默認開啓
58 
59 
60     }
61 
62     /**
63      * 權限管理器  AuthorizationServerConfigurerAdapter認證中心須要的AuthenticationManager須要
64      */
65     @Override
66     protected void configure(AuthenticationManagerBuilder auth) throws Exception {
67         //目的是爲了前端獲取數據時獲取到整個form-data的數據,提供驗證器
68         auth.authenticationProvider(customerAuthenticationProvider);
69         //配置登陸user驗證處理器  以及密碼加密器  好讓認證中心進行驗證
70         auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);
71     }
72 
73     /**
74      * 須要配置這個支持password模式
75      * support password grant type
76      *
77      * @return
78      * @throws Exception
79      */
80     @Override
81     @Bean
82     public AuthenticationManager authenticationManagerBean() throws Exception {
83         return authenticationManager();
84     }
85 }

  注意: 1. UserDetailsService接口中主要包含有一個UserDetails loadUserByUsername(String var1) 方法,其中var1表明username即登陸時輸入的用戶名,本項目的配置以下ajax

      

 1 import cn.com.megalith.auth.UserDetail;
 2 import cn.com.megalith.domain.entity.User;
 3 import cn.com.megalith.service.IUserService;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.security.core.userdetails.UserDetails;
 6 import org.springframework.security.core.userdetails.UserDetailsService;
 7 import org.springframework.security.core.userdetails.UsernameNotFoundException;
 8 import org.springframework.stereotype.Component;
 9 
10 /**
11  * @Description:
12  * @author: zhoum
13  * @Date: 2018-11-22
14  * @Time: 15:07
15  */
16 @Component("signUserDetailService")
17 public class SignUserDetaiServiceConfig implements UserDetailsService {
18 
19     @Autowired
20     private IUserService userService;
21 
22     /**
23      * 啓動刷新token受權類型,會判斷用戶是否仍是存活的
24      */
25     @Override
26     public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
27         User currentUser = userService.getByName(s);
28         if ( currentUser == null ) {
29             throw new UsernameNotFoundException("用戶沒用找到");
30         }
31 
32         return new UserDetail(currentUser);
33     }
34 }

  這兒須要返回一個UserDetails的實現類用於spring security中驗證 本項目的配置以下  其中的user爲自定義的user實體算法

  1 import cn.com.megalith.domain.entity.User;
  2 import org.springframework.context.ApplicationContext;
  3 import org.springframework.security.core.GrantedAuthority;
  4 import org.springframework.security.core.authority.SimpleGrantedAuthority;
  5 import org.springframework.security.core.userdetails.UserDetails;
  6 
  7 import java.util.ArrayList;
  8 import java.util.Collection;
  9 import java.util.List;
 10 
 11 /**
 12  * @Description: 用戶信息的實體類
 13  * @author: zhoum
 14  * @Date: 2018-11-22
 15  * @Time: 15:46
 16  */
 17 public class UserDetail implements UserDetails {
 18 
 19     private User user;
 20     
 21     private String id;
 22 
 23     /**
 24      * 經過構造方法在UserDetailsService的方法中將查到的user注入進去
 25      */
 26     public UserDetail(User user) {
 27         this.user = user;
 28         if ( user != null ) {
 29             this.id = user.getId();
 30         }
 31     }
 32     /**
 33      * 對當前的用戶賦予其應有的權限
 34      */
 35     @Override
 36     public Collection<? extends GrantedAuthority> getAuthorities() {
 37         //添加權限 這兒暫時寫死  應該從數據庫中拿到
 38         List authorisList = new ArrayList();
 39         authorisList.add(new SimpleGrantedAuthority("ROLE_AA"));
 40         authorisList.add(new SimpleGrantedAuthority("ROLE_BB"));
 41         authorisList.add(new SimpleGrantedAuthority("ROLE_CC"));
 42         authorisList.add(new SimpleGrantedAuthority("ROLE_DD"));
 43         return authorisList;
 44     }
 45     /**
 46      * 獲取密碼
 47      */
 48     @Override
 49     public String getPassword() {
 50         return user.getPassword();
 51     }
 52     /**
 53      * 獲取用戶名
 54      */
 55     @Override
 56     public String getUsername() {
 57         return user.getUsername();
 58     }
 59     /**
 60      * 帳戶是否未過時
 61      */
 62     @Override
 63     public boolean isAccountNonExpired() {
 64         return true;
 65     }
 66     /**
 67      * 帳戶是否未鎖定
 68      */
 69     @Override
 70     public boolean isAccountNonLocked() {
 71         return true;
 72     }
 73     /**
 74      * 證書是否未過時
 75      */
 76     @Override
 77     public boolean isCredentialsNonExpired() {
 78         return true;
 79     }
 80 
 81     /**
 82      * 是否有效   可對應數據庫中的delete_flag字段
 83      */
 84     @Override
 85     public boolean isEnabled() {
 86         return true;
 87     }
 88 
 89     public User getUser() {
 90         return user;
 91     }
 92 
 93     public void setUser(User user) {
 94         this.user = user;
 95     }
 96 
 97     public String getId() {
 98         return id;
 99     }
100 
101     public void setId(String id) {
102         this.id = id;
103     }
104 }

    注意: 2. PasswordEncoder 主要爲加密管理器  用於密碼的加密匹配  本項目的配置以下spring

 1 import org.springframework.context.annotation.Configuration;
 2 import org.springframework.security.crypto.password.PasswordEncoder;
 3 import org.springframework.stereotype.Component;
 4 
 5 import java.security.MessageDigest;
 6 
 7 /**
 8  * @Description:MD5加密
 9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:36
12  */
13 @Component
14 public class EncodePassword implements PasswordEncoder {
15 
16     /**
17      * md5加密
18      */
19     @Override
20     public String encode(CharSequence charSequence) {
21 
22         return MD5(charSequence.toString());
23     }
24 
25     /**
26      * 匹配規則
27      */
28     @Override
29     public boolean matches(CharSequence charSequence , String s) {
30         return MD5(charSequence.toString()).equals(s);
31     }
32 
33     /**
34      * md5加密過程
35      */
36     public static String MD5(String key) {
37         char hexDigits[] = {
38                 '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f'
39         };
40         try {
41             byte[] btInput = key.getBytes();
42             // 得到MD5摘要算法的 MessageDigest 對象
43             MessageDigest mdInst = MessageDigest.getInstance("MD5");
44             // 使用指定的字節更新摘要
45             mdInst.update(btInput);
46             // 得到密文
47             byte[] md = mdInst.digest();
48             // 把密文轉換成十六進制的字符串形式
49             int j = md.length;
50             char str[] = new char[ j * 2 ];
51             int k = 0;
52             for (int i = 0; i < j; i++) {
53                 byte byte0 = md[ i ];
54                 str[ k++ ] = hexDigits[ byte0 >>> 4 & 0xf ];
55                 str[ k++ ] = hexDigits[ byte0 & 0xf ];
56             }
57             return new String(str);
58         } catch (Exception e) {
59             return null;
60         }
61     }
62 }

 

    注意: 3.CustomerAuthenticationProvider 主要是爲AuthenticationProvider借接口的實現類  爲spring security提供密碼驗證器  是核心配置,這兒須要注意  系統中已經有相應的實現類,若是不配置,則系統中會默認使用org.springframework.security.authentication.dao.DaoAuthenticationProvider這個類來進行驗證,DaoAuthenticationProvider這個類繼承了org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider這個抽象類,因此咱們要自定義provider驗證流程能夠實現AuthenticationProvider接口或者繼承AbstractUserDetailsAuthenticationProvider抽象類都可,下面是兩種模式的代碼sql

    (1)實現AuthenticationProvider形式  本身寫驗證流程

      

 1 import org.slf4j.Logger;
 2 import org.slf4j.LoggerFactory;
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.security.authentication.AuthenticationProvider;
 5 import org.springframework.security.authentication.BadCredentialsException;
 6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 7 import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
 8 import org.springframework.security.core.Authentication;
 9 import org.springframework.security.core.AuthenticationException;
10 import org.springframework.security.core.userdetails.UserDetails;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.crypto.password.PasswordEncoder;
13 import org.springframework.stereotype.Component;
14 
15 import javax.annotation.Resource;
16 import java.util.Objects;
17 
18 /**
19  * @Description: AuthenticationManagerBuilder中的AuthenticationProvider是進行認證的核心
20  * @author: zhoum
21  * @Date: 2018-11-23
22  * @Time: 9:11
23  */
24 @Component("myCustomerAuthenticationProvider")
25 public class CustomerAuthenticationProvider implements AuthenticationProvider {
26 
27     @Resource(name = "signUserDetailService")
28     private UserDetailsService userDetailsService;
29 
30    public static final Logger LOGGER = LoggerFactory.getLogger(CustomerAuthenticationProvider.class);
31 
32     /**
33      *authentication是前臺拿過來的號碼密碼bean  主要驗證流程代碼  注意這兒懶得用加密驗證!!!
34      */
35     @Override
36     public Authentication authenticate(Authentication authentication) throws AuthenticationException {
37         LOGGER.info("用戶輸入的用戶名是:" + authentication.getName());
38         LOGGER.info("用戶輸入的密碼是:" + authentication.getCredentials());
39         // 根據用戶輸入的用戶名獲取該用戶名已經在服務器上存在的用戶詳情,若是沒有則返回null
40         UserDetails userDetails = userDetailsService.loadUserByUsername(authentication.getName());
41         try {
42             LOGGER.info("服務器上已經保存的用戶名是:" + userDetails.getUsername());
43             LOGGER.info("服務器上保存的該用戶名對應的密碼是: " + userDetails.getPassword());
44             LOGGER.info("服務器上保存的該用戶對應的權限是:" + userDetails.getAuthorities());
45             if(authentication.getCredentials().equals(userDetails.getPassword())){
46                 //驗證成功  將返回一個UsernamePasswordAuthenticaionToken對象
47                 LOGGER.info("LOGIN SUCCESS !!!!!!!!!!!!!!!!!!!");
48                 //分別返回用戶實體   輸入的密碼   以及用戶的權限
49                 return new UsernamePasswordAuthenticationToken(userDetails,authentication.getCredentials(),userDetails.getAuthorities());
50             }
51         } catch (Exception e){
52             LOGGER.error("author failed, -------------------the error message is:-------- " + e);
53             throw e;
54         }
55         //若是驗證不一樣過則返回null或者拋出異常
56         return null;
57     }
58 
59     /**
60      *
61      **/
62     @Override
63     public boolean supports(Class<?> aClass) {
64         return true;
65     }
66 }

    這兒會返回一個UsernamePasswordAuthenticationToken對象,

    

    (2)繼承AbstractUserDetailsAuthenticationProvider抽象類  這兒主要是複寫取user和驗證流程  這兒直接用了DaoAuthenticationProvider中的驗證代碼  有興趣的能夠本身從新寫驗證代碼

 1 import org.slf4j.Logger;
 2 import org.slf4j.LoggerFactory;
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.security.authentication.AuthenticationProvider;
 5 import org.springframework.security.authentication.BadCredentialsException;
 6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
 7 import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
 8 import org.springframework.security.core.Authentication;
 9 import org.springframework.security.core.AuthenticationException;
10 import org.springframework.security.core.userdetails.UserDetails;
11 import org.springframework.security.core.userdetails.UserDetailsService;
12 import org.springframework.security.crypto.password.PasswordEncoder;
13 import org.springframework.stereotype.Component;
14 
15 import javax.annotation.Resource;
16 import java.util.Objects;
17 
18 /**
19  * @Description: AuthenticationManagerBuilder中的AuthenticationProvider是進行認證的核心
20  * @author: zhoum
21  * @Date: 2018-11-23
22  * @Time: 9:11
23  */
24 @Component("myCustomerAuthenticationProvider")
25 public class CustomerAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
26 
27     @Resource(name = "signUserDetailService")
28     private UserDetailsService userDetailsService;
29 
30     @Autowired
31     private PasswordEncoder passwordEncoder;
32 
33     /**
34      * 手動實現認證
35      */
36     @Override
37     protected void additionalAuthenticationChecks(UserDetails userDetails , UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
38         if ( authentication.getCredentials() == null ) {
39             this.logger.debug("Authentication failed: no credentials provided");
40             throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials"));
41         } else {
42             String presentedPassword = authentication.getCredentials().toString();
43             if ( !this.passwordEncoder.matches(presentedPassword , userDetails.getPassword()) ) {
44                 this.logger.debug("Authentication failed: password does not match stored value");
45                 throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials" , "Bad credentials"));
46             }
47         }
48     }
49 
50     /**
51      * 手動加載user
52      */
53     @Override
54     protected UserDetails retrieveUser(String s , UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
55         return userDetailsService.loadUserByUsername(s);
56     }
57 }

    注意4:configure(HttpSecurity http)會覆蓋ResourceServerConfig中的配置

  4.4 配置ajax跨域

  

 1 import org.springframework.context.annotation.Bean;
 2 import org.springframework.context.annotation.Configuration;
 3 import org.springframework.web.cors.CorsConfiguration;
 4 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 5 import org.springframework.web.filter.CorsFilter;
 6 
 7 /**
 8  * @Description:
 9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:09
12  */
13 @Configuration
14 public class WebOrignAllowConfig {
15     /**
16      * @return org.springframework.web.filter.CorsFilter
17      * @Author zhoum
18      * @Description 解決跨域問題
19      * @Date 17:10 2018-11-22
20      * @Param []
21      **/
22     @Bean
23     public CorsFilter corsFilter() {
24         final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
25         final CorsConfiguration corsConfiguration = new CorsConfiguration();
26         corsConfiguration.setAllowCredentials(true);
27         corsConfiguration.addAllowedOrigin("*");
28         corsConfiguration.addAllowedHeader("*");
29         corsConfiguration.addAllowedMethod("*");
30         urlBasedCorsConfigurationSource.registerCorsConfiguration("/**" , corsConfiguration);
31         return new CorsFilter(urlBasedCorsConfigurationSource);
32     }
33 }

 

5. 其餘項目類即根據User的bean建立出UserController   UserService  UserMapper等  代碼能夠查看git上的源碼

6. 項目的屬性配置 application.yml

  

spring:
  #色彩日誌輸出
  output:
    ansi:
      enabled: always
  datasource:
    username: root
    password: 123456
    url: jdbc:mysql://localhost:3306/tjfx?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&allowPublicKeyRetrieval=true
    type: com.alibaba.druid.pool.DruidDataSource
    #鏈接池配置
    driverClassName: com.mysql.jdbc.Driver
    # 初始化大小,最小,最大
    initialSize: 5
    minIdle: 5
    maxActive: 20
    # 配置獲取鏈接等待超時的時間
    maxWait: 60000
    # 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒
    timeBetweenEvictionRunsMillis: 60000
    # 配置一個鏈接在池中最小生存的時間,單位是毫秒
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    # 打開PSCache,而且指定每一個鏈接上PSCache的大小
    poolPreparedStatements: true
    maxPoolPreparedStatementPerConnectionSize: 20
    # 配置監控統計攔截的filters,去掉後監控界面sql沒法統計,'wall'用於防火牆
    filters: stat,log4j
    # 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

mybatis:
  mapper-locations: classpath*:**/mappers/*.xml
  type-aliases-package: cn.com.megalith.domain.entity
  configuration:
    map-underscore-to-camel-case: true
    use-generated-keys: true
    use-column-label: true
    cache-enabled: true
    call-setters-on-nulls: true
logging:
  level:
    root: info
    org:
      springframework: info
server:
  port: 8081

7.啓動類SecurityApplication

  

 1 import org.mybatis.spring.annotation.MapperScan;
 2 import org.springframework.boot.SpringApplication;
 3 import org.springframework.boot.autoconfigure.SpringBootApplication;
 4 import org.springframework.transaction.annotation.EnableTransactionManagement;
 5 
 6 @SpringBootApplication
 7 @MapperScan("cn.com.megalith.dao")
 8 @EnableTransactionManagement
 9 public class SecurityApplication {
10 
11     public static void main(String[] args) {
12         SpringApplication.run(SecurityApplication.class , args);
13     }

8.項目總體效果以下(不要在乎紅色,編輯器故障了)

 

 9.進行測試  

  (1) 獲取token

  

  注意:其中的client_id  與 client_secret即在oauth_client_details中的數據  這兒是我本身手動插入的一條  通常在申請別的網站的受權時應該會獲得這個  client_secret是md5加密後的結果

  若是號碼密碼正確則會返回  紅色框內即爲獲得的token

  

 

  若是密碼錯誤 則會返回剛自定義的錯誤返回結果

  

  (2)進行方法驗證

 1 @RestController
 2 @RequestMapping("/user")
 3 public class UserController {
 4 
 5     @GetMapping("/current")
 6     @Secured("ROLE_AA")
 7     public String getCurrentUser(Principal principal) {
 8         System.out.println(principal);
 9         return "111";
10     }
11 }

  訪問方式:記得加上token  否則會驗證不經過

  

 

 

 

 若是有權限 則會返回

  且principal即爲獲取到的當前用戶的對象  具體結構以下

 

   驗證失敗則會返回

  若是無權限則會返回

  

以上爲受權認證端與資源服務端在同一項目時,若是爲不一樣的項目則資源服務的項目能夠單獨以下建立項目

 

 

項目customer_simple(資源服務項目)

1.建立項目

   與建立上面受權項目徹底相似,依賴同樣,因此略過

2.配置類

  

  (1)ResourceServiceConfig

  

 1 import org.springframework.context.annotation.Configuration;
 2 import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
 3 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
 4 import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
 5 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 6 import org.springframework.web.cors.CorsConfiguration;
 7 
 8 import javax.servlet.http.HttpServletResponse;
 9 
10 /**
11  * @Description: 配置資源服務器
12  * @author: zhoum
13  * @Date: 2018-11-26
14  * @Time: 15:08
15  */
16 @Configuration
17 @EnableResourceServer
18 @EnableGlobalMethodSecurity(securedEnabled = true)
19 public class ResourceServiceConfig extends ResourceServerConfigurerAdapter {
20 
21     @Override
22     public void configure(HttpSecurity http) throws Exception {
23         http
24                 .csrf().disable()
25                 .exceptionHandling()
26                 .authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
27                 .and()
28                 .authorizeRequests()
29                 .antMatchers("/**").permitAll()
30                 //跨域配置
31                 .and().cors().configurationSource(request -> {
32             CorsConfiguration corsConfiguration = new CorsConfiguration();
33             corsConfiguration.addAllowedHeader("*");
34             corsConfiguration.addAllowedOrigin(request.getHeader("Origin"));
35             corsConfiguration.addAllowedMethod("*");
36             corsConfiguration.setAllowCredentials(true);
37             corsConfiguration.setMaxAge(3600L);
38             return corsConfiguration;
39         });
40     }
41 }

  (2) 跨域配置類

  

 1 import org.springframework.context.annotation.Bean;
 2 import org.springframework.context.annotation.Configuration;
 3 import org.springframework.web.cors.CorsConfiguration;
 4 import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
 5 import org.springframework.web.filter.CorsFilter;
 6 
 7 /**
 8  * @Description:
 9  * @author: zhoum
10  * @Date: 2018-11-22
11  * @Time: 17:09
12  */
13 @Configuration
14 public class WebOrignAllowConfig {
15     /**
16      * @return org.springframework.web.filter.CorsFilter
17      * @Author zhoum
18      * @Description 解決跨域問題
19      * @Date 17:10 2018-11-22
20      * @Param []
21      **/
22     @Bean
23     public CorsFilter corsFilter() {
24         final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource = new UrlBasedCorsConfigurationSource();
25         final CorsConfiguration corsConfiguration = new CorsConfiguration();
26         corsConfiguration.setAllowCredentials(true);
27         corsConfiguration.addAllowedOrigin("*");
28         corsConfiguration.addAllowedHeader("*");
29         corsConfiguration.addAllowedMethod("*");
30         urlBasedCorsConfigurationSource.registerCorsConfiguration("/**" , corsConfiguration);
31         return new CorsFilter(urlBasedCorsConfigurationSource);
32     }
33 }

  (3)application.yml配置

  

 1 spring:
 2   #色彩日誌輸出
 3   output:
 4     ansi:
 5       enabled: always
 6   datasource:
 7     username: root
 8     password: 123456
 9     url: jdbc:mysql://localhost:3306/tjfx?autoReconnect=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&allowPublicKeyRetrieval=true
10     type: com.alibaba.druid.pool.DruidDataSource
11     #鏈接池配置
12     driverClassName: com.mysql.jdbc.Driver
13     # 初始化大小,最小,最大
14     initialSize: 5
15     minIdle: 5
16     maxActive: 20
17     # 配置獲取鏈接等待超時的時間
18     maxWait: 60000
19     # 配置間隔多久才進行一次檢測,檢測須要關閉的空閒鏈接,單位是毫秒
20     timeBetweenEvictionRunsMillis: 60000
21     # 配置一個鏈接在池中最小生存的時間,單位是毫秒
22     minEvictableIdleTimeMillis: 300000
23     validationQuery: SELECT 1 FROM DUAL
24     testWhileIdle: true
25     testOnBorrow: false
26     testOnReturn: false
27     # 打開PSCache,而且指定每一個鏈接上PSCache的大小
28     poolPreparedStatements: true
29     maxPoolPreparedStatementPerConnectionSize: 20
30     # 配置監控統計攔截的filters,去掉後監控界面sql沒法統計,'wall'用於防火牆
31     filters: stat,log4j
32     # 經過connectProperties屬性來打開mergeSql功能;慢SQL記錄
33     connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
34 
35 mybatis:
36   mapper-locations: classpath*:**/mappers/*.xml
37   type-aliases-package: cn.com.megalith.domain.entity
38   configuration:
39     map-underscore-to-camel-case: true
40     use-generated-keys: true
41     use-column-label: true
42     cache-enabled: true
43     call-setters-on-nulls: true
44 #spring security oauth2配置
45 security:
46   oauth2:
47     #token檢查的受權端url
48     authorization:
49       check-token-access: http://127.0.01:8081/oauth/check_token
50     #對應的註冊碼與註冊密碼
51     client:
52       id: 1
53       client-secret: 2
54       authentication-scheme: form
55     #得到受權端的當前用戶信息url
56     resource:
57       user-info-uri: http://127.0.01:8081/user/me
58       id: oa
59 
60 logging:
61   level:
62     root: info
63     org:
64       springframework: info
65 server:
66   port: 8082

  這兒注意http://127.0.01:8081/user/me即受權服務端中的一個接口 須要提供,返回用戶信息,代碼在受權端項目UserController下添加以下方法便可

  

1     /**
2      *客戶端根據token獲取用戶
3      */
4     @RequestMapping("/me")
5     public Principal user2(OAuth2Authentication principal) {
6         return principal;
7     }

 

  (4)啓動類配置

 1 import org.mybatis.spring.annotation.MapperScan;
 2 import org.springframework.boot.SpringApplication;
 3 import org.springframework.boot.autoconfigure.SpringBootApplication;
 4 
 5 @SpringBootApplication
 6 @MapperScan("cn.com.megalith.dao")
 7 public class CustomerSimpleApplication {
 8 
 9     public static void main(String[] args) {
10         SpringApplication.run(CustomerSimpleApplication.class , args);
11     }
12 }

 

 2.項目的其餘輔助類主要爲User的controller,service,dao,entity等  源碼裏有

 3.項目弄好後結構以下

4.測試

  依舊使用UserController,代碼以下

  

 1 @RestController
 2 @RequestMapping("/user")
 3 public class UserController {
 4 
 5     @GetMapping("/current")
 6     @Secured("ROLE_AA")
 7     public String getCurrentUser(Principal principal) {
 8         System.out.println(principal);
 9         return "111";
10     }
11 }

  

  測試方法以下,使用上面得到token的方式獲取token後訪問以下

  

  注意這兒變成了8082端口,說明是另外的一個資源服務端的項目

  驗證成功

  驗證失敗後返回

  

  若是沒有權限則會返回

  

 

到此基於springboot  spring security  +oauth2.0的密碼模式受權  且受權端與資源提供端分離的模式就完成了 有問題留言

項目的源碼地址爲     https://github.com/hetutu5238/zmc_security_oauth2.git           security_simple爲認證受權端   customer_simple爲資源服務端                 

項目的mysql腳本在   security_simple下的resource/sql/tjfx.sql

 

 

 

 

在獲取token時  如何返回以下格式的數據結構?

 

 

1.定義返回類

 1 public class Result<T> implements Serializable {
 2 
 3     private String message ;
 4 
 5     private Integer code ;
 6 
 7     private T result;
 8 
 9 
10     public static<T> Result<T> ok(T data) {
11         Result<T> r = new Result<>();
12         r.setMessage("ok");
13         r.setCode(HttpStatus.OK.value());
14         r.setResult(data);
15         return r;
16     }
17 
18     public String getMessage() {
19         return message;
20     }
21 
22     public void setMessage(String message) {
23         this.message = message;
24     }
25 
26     public Integer getCode() {
27         return code;
28     }
29 
30     public void setCode(Integer code) {
31         this.code = code;
32     }
33 
34     public T getResult() {
35         return result;
36     }
37 
38     public void setResult(T result) {
39         this.result = result;
40     }
41 }

2.pom.xml新增

1         <dependency>
2             <groupId>org.springframework.boot</groupId>
3             <artifactId>spring-boot-starter-aop</artifactId>
4         </dependency>

3.增長配置類   項目源碼我注掉了  有須要的能夠打開

 1 @Aspect
 2 @Component
 3 public class CodeAspect {
 4 
 5     @Pointcut("execution(public * org.springframework.security.oauth2.provider..*.*TokenEndpoint.postAccessToken(..))")
 6     public void excudeService() {
 7     }
 8 
 9     @Around("excudeService()")
10     public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
11 
12         Object result = pjp.proceed();
13         if ( result instanceof ResponseEntity ){
14             ResponseEntity res = (ResponseEntity)result;
15             if ( res.getStatusCode() .equals(HttpStatus.OK) ){
16                 Object body = res.getBody();
17                 if ( body instanceof DefaultOAuth2AccessToken ){
18                     DefaultOAuth2AccessToken data = (DefaultOAuth2AccessToken)body;
19                     ResponseEntity codeResult = new ResponseEntity(Result.ok(data.getValue()),HttpStatus.OK);
20                     return codeResult;
21                 }
22             }
23 
24         }
25         return result;
26     }
27 }

這樣便可

相關文章
相關標籤/搜索