這兒選擇springboot版本我選擇的是2.0.6前端
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>
建表sql語句的地址爲 https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql 這兒記得不一樣的數據庫中的特殊字段須要修改一下,建好的表以下java
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 }
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
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中的配置
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 }
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
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 }
注意:其中的client_id 與 client_secret即在oauth_client_details中的數據 這兒是我本身手動插入的一條 通常在申請別的網站的受權時應該會獲得這個 client_secret是md5加密後的結果
若是號碼密碼正確則會返回 紅色框內即爲獲得的token
若是密碼錯誤 則會返回剛自定義的錯誤返回結果
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即爲獲取到的當前用戶的對象 具體結構以下
驗證失敗則會返回
若是無權限則會返回
以上爲受權認證端與資源服務端在同一項目時,若是爲不一樣的項目則資源服務的項目能夠單獨以下建立項目
與建立上面受權項目徹底相似,依賴同樣,因此略過
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 }
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 }
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
1 /** 2 *客戶端根據token獲取用戶 3 */ 4 @RequestMapping("/me") 5 public Principal user2(OAuth2Authentication principal) { 6 return principal; 7 }
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 }
依舊使用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 }
這樣便可