├── README.md ├── pom.xml └── src ├── main ├── java │ └── com │ │ └── unionman │ │ └── springbootsecurityauth2 │ │ ├── SpringbootSecurityAuth2Application.java │ │ ├── config │ │ ├── HttpMessageConverterConfig.java │ │ ├── OAuth2Config.java │ │ ├── RedisConfig.java │ │ ├── SecurityConfig.java │ │ └── ServerConfig.java │ │ ├── controller │ │ └── AuthController.java │ │ ├── domain │ │ ├── Base.java │ │ ├── CustomUserDetail.java │ │ ├── Token.java │ │ └── bean │ │ │ └── RefreshTokenBean.java │ │ ├── dto │ │ ├── LoginUserDTO.java │ │ ├── ModifyUserDTO.java │ │ └── UserDTO.java │ │ ├── entity │ │ ├── Role.java │ │ └── User.java │ │ ├── enums │ │ ├── ResponseEnum.java │ │ └── UrlEnum.java │ │ ├── handler │ │ └── CustomAuthExceptionHandler.java │ │ ├── repository │ │ ├── RoleRepository.java │ │ ├── UserRepository.java │ │ └── base │ │ │ └── BaseRepository.java │ │ ├── service │ │ ├── RoleService.java │ │ ├── UserService.java │ │ └── impl │ │ │ ├── RoleServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── utils │ │ ├── AssertUtils.java │ │ ├── BeanUtils.java │ │ └── RedisUtil.java │ │ └── vo │ │ ├── LoginUserVO.java │ │ ├── ResponseVO.java │ │ ├── RoleVO.java │ │ └── UserVO.java └── resources │ ├── application.yml │ └── springbootsecurityauth.sql └── test └── java └── com └── unionman └── springbootsecurityauth2 └── SpringbootSecurityAuth2ApplicationTests.java /README.md: -------------------------------------------------------------------------------- 1 | # springboot-security-oauth2 2 | 基于Springboot集成security、oauth2实现认证鉴权、资源管理 3 | 具体详细博文,可以参见 https://www.cnblogs.com/xiaofengxzzf/p/10733955.html 4 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.0.5.RELEASE 9 | 10 | 11 | com.unionman 12 | springboot-security-auth2 13 | 0.0.1-SNAPSHOT 14 | springboot-security-auth2 15 | Demo project for Spring Boot 16 | 17 | 18 | 1.8 19 | 20 | 21 | 22 | 23 | org.springframework.boot 24 | spring-boot-starter 25 | 26 | 27 | org.springframework.boot 28 | spring-boot-starter-test 29 | test 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-data-redis 42 | 43 | 44 | 45 | 46 | org.projectlombok 47 | lombok 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-security 54 | 55 | 56 | 57 | org.springframework.security.oauth 58 | spring-security-oauth2 59 | 2.3.3.RELEASE 60 | 61 | 62 | 63 | 64 | mysql 65 | mysql-connector-java 66 | 5.1.47 67 | runtime 68 | 69 | 70 | org.springframework.boot 71 | spring-boot-starter-data-jpa 72 | 73 | 74 | 75 | 76 | com.alibaba 77 | fastjson 78 | 1.2.47 79 | 80 | 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-maven-plugin 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/SpringbootSecurityAuth2Application.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class SpringbootSecurityAuth2Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(SpringbootSecurityAuth2Application.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/config/HttpMessageConverterConfig.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.serializer.SerializerFeature; 5 | import com.alibaba.fastjson.support.config.FastJsonConfig; 6 | import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 8 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.http.MediaType; 12 | import org.springframework.http.converter.HttpMessageConverter; 13 | import org.springframework.http.converter.StringHttpMessageConverter; 14 | 15 | import java.nio.charset.Charset; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | /** 20 | * @description: 自定义消息转换器,该配置是将json与form-data之间数据相互转换 21 | */ 22 | @Configuration 23 | @ConditionalOnClass(JSON.class) 24 | public class HttpMessageConverterConfig { 25 | 26 | /** 27 | * @return StringHttpMessageConverter 28 | * @descrption: http消息转换器。 29 | * @author Rong.Jia 30 | * @date 2019/01/07 25:16:44 31 | */ 32 | @Bean 33 | public StringHttpMessageConverter stringHttpMessageConverter() { 34 | StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8")); 35 | return converter; 36 | } 37 | 38 | @Bean 39 | public HttpMessageConverters fastJsonHttpMessageConverters() { 40 | 41 | // 1.定义一个converters转换消息的对象 42 | FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter(); 43 | 44 | // 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据 45 | FastJsonConfig fastJsonConfig = new FastJsonConfig(); 46 | 47 | fastJsonConfig.setSerializerFeatures(SerializerFeature.QuoteFieldNames, 48 | SerializerFeature.WriteEnumUsingToString, 49 | //SerializerFeature.WriteMapNullValue, 50 | SerializerFeature.WriteDateUseDateFormat, 51 | SerializerFeature.DisableCircularReferenceDetect 52 | ); 53 | // 3.在converter中添加配置信息 54 | fastConverter.setFastJsonConfig(fastJsonConfig); 55 | List fastMediaTypes = new ArrayList<>(); 56 | fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); 57 | fastConverter.setSupportedMediaTypes(fastMediaTypes); 58 | 59 | // 4.将converter赋值给HttpMessageConverter 60 | HttpMessageConverter converter = fastConverter; 61 | 62 | // 5.返回HttpMessageConverters对象 63 | return new HttpMessageConverters(converter); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/config/OAuth2Config.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.config; 2 | 3 | import com.unionman.springbootsecurityauth2.handler.CustomAuthExceptionHandler; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | import org.springframework.data.redis.connection.RedisConnectionFactory; 8 | import org.springframework.http.HttpMethod; 9 | import org.springframework.security.authentication.AuthenticationManager; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.http.SessionCreationPolicy; 12 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 13 | import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 14 | import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; 15 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; 16 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 17 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 18 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 19 | import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; 20 | import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; 21 | import org.springframework.security.oauth2.provider.token.DefaultTokenServices; 22 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; 23 | 24 | import java.util.concurrent.TimeUnit; 25 | 26 | 27 | 28 | /** 29 | * @author Zhifeng.Zeng 30 | * @description OAuth2服务器配置 31 | */ 32 | @Configuration 33 | public class OAuth2Config { 34 | 35 | public static final String ROLE_ADMIN = "ADMIN"; 36 | //访问客户端密钥 37 | public static final String CLIENT_SECRET = "123456"; 38 | //访问客户端ID 39 | public static final String CLIENT_ID ="client_1"; 40 | //鉴权模式 41 | public static final String GRANT_TYPE[] = {"password","refresh_token"}; 42 | 43 | /** 44 | * @description 资源服务器 45 | */ 46 | @Configuration 47 | @EnableResourceServer 48 | protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { 49 | 50 | @Autowired 51 | private CustomAuthExceptionHandler customAuthExceptionHandler; 52 | 53 | @Override 54 | public void configure(ResourceServerSecurityConfigurer resources) { 55 | resources.stateless(false) 56 | .accessDeniedHandler(customAuthExceptionHandler) 57 | .authenticationEntryPoint(customAuthExceptionHandler); 58 | } 59 | 60 | @Override 61 | public void configure(HttpSecurity http) throws Exception { 62 | http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) 63 | .and() 64 | //请求权限配置 65 | .authorizeRequests() 66 | //下边的路径放行,不需要经过认证 67 | .antMatchers("/oauth/*", "/auth/user/login").permitAll() 68 | //OPTIONS请求不需要鉴权 69 | .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() 70 | //用户的增删改接口只允许管理员访问 71 | .antMatchers(HttpMethod.POST, "/auth/user").hasAnyAuthority(ROLE_ADMIN) 72 | .antMatchers(HttpMethod.PUT, "/auth/user").hasAnyAuthority(ROLE_ADMIN) 73 | .antMatchers(HttpMethod.DELETE, "/auth/user").hasAnyAuthority(ROLE_ADMIN) 74 | //获取角色 权限列表接口只允许系统管理员及高级用户访问 75 | .antMatchers(HttpMethod.GET, "/auth/role").hasAnyAuthority(ROLE_ADMIN) 76 | //其余接口没有角色限制,但需要经过认证,只要携带token就可以放行 77 | .anyRequest() 78 | .authenticated(); 79 | 80 | } 81 | } 82 | 83 | /** 84 | * @description 认证授权服务器 85 | */ 86 | @Configuration 87 | @EnableAuthorizationServer 88 | protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter { 89 | 90 | @Autowired 91 | private AuthenticationManager authenticationManager; 92 | 93 | @Autowired 94 | private RedisConnectionFactory connectionFactory; 95 | 96 | @Override 97 | public void configure(ClientDetailsServiceConfigurer clients) throws Exception { 98 | String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode(CLIENT_SECRET); 99 | //配置客户端,使用密码模式验证鉴权 100 | clients.inMemory() 101 | .withClient(CLIENT_ID) 102 | //密码模式及refresh_token模式 103 | .authorizedGrantTypes(GRANT_TYPE[0], GRANT_TYPE[1]) 104 | .scopes("all") 105 | .secret(finalSecret); 106 | } 107 | 108 | @Bean 109 | public RedisTokenStore redisTokenStore() { 110 | return new RedisTokenStore(connectionFactory); 111 | } 112 | 113 | /** 114 | * @description token及用户信息存储到redis,当然你也可以存储在当前的服务内存,不推荐 115 | * @param endpoints 116 | */ 117 | @Override 118 | public void configure(AuthorizationServerEndpointsConfigurer endpoints) { 119 | //token信息存到服务内存 120 | /*endpoints.tokenStore(new InMemoryTokenStore()) 121 | .authenticationManager(authenticationManager);*/ 122 | 123 | //token信息存到redis 124 | endpoints.tokenStore(redisTokenStore()).authenticationManager(authenticationManager); 125 | //配置TokenService参数 126 | DefaultTokenServices tokenService = new DefaultTokenServices(); 127 | tokenService.setTokenStore(endpoints.getTokenStore()); 128 | tokenService.setSupportRefreshToken(true); 129 | tokenService.setClientDetailsService(endpoints.getClientDetailsService()); 130 | tokenService.setTokenEnhancer(endpoints.getTokenEnhancer()); 131 | //1小时 132 | tokenService.setAccessTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(1)); 133 | //1小时 134 | tokenService.setRefreshTokenValiditySeconds((int) TimeUnit.HOURS.toSeconds(1)); 135 | tokenService.setReuseRefreshToken(false); 136 | endpoints.tokenServices(tokenService); 137 | } 138 | 139 | @Override 140 | public void configure(AuthorizationServerSecurityConfigurer oauthServer) { 141 | //允许表单认证 142 | oauthServer.allowFormAuthenticationForClients().tokenKeyAccess("isAuthenticated()") 143 | .checkTokenAccess("permitAll()"); 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.cache.annotation.CachingConfigurerSupport; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.data.redis.connection.RedisConnectionFactory; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 12 | import org.springframework.data.redis.serializer.RedisSerializer; 13 | import org.springframework.data.redis.serializer.StringRedisSerializer; 14 | 15 | 16 | /** 17 | * @description redis 配置 18 | */ 19 | @Configuration 20 | public class RedisConfig extends CachingConfigurerSupport { 21 | 22 | /** 23 | * @description redis模板,存储关键字是字符串,值jackson2JsonRedisSerializer是序列化后的值 24 | */ 25 | @Bean 26 | public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { 27 | RedisTemplate redisTemplate = new RedisTemplate<>(); 28 | redisTemplate.setConnectionFactory(connectionFactory); 29 | //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) 30 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 31 | ObjectMapper objectMapper = new ObjectMapper(); 32 | objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 33 | objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 34 | jackson2JsonRedisSerializer.setObjectMapper(objectMapper); 35 | //使用StringRedisSerializer来序列化和反序列化redis的key值 36 | RedisSerializer redisSerializer = new StringRedisSerializer(); 37 | redisTemplate.setKeySerializer(redisSerializer); 38 | redisTemplate.setHashKeySerializer(redisSerializer); 39 | redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); 40 | redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); 41 | redisTemplate.afterPropertiesSet(); 42 | return redisTemplate; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/config/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.config; 2 | 3 | import com.unionman.springbootsecurityauth2.domain.CustomUserDetail; 4 | import com.unionman.springbootsecurityauth2.entity.User; 5 | import com.unionman.springbootsecurityauth2.repository.UserRepository; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.security.authentication.AuthenticationManager; 11 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 12 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 13 | import org.springframework.security.core.GrantedAuthority; 14 | import org.springframework.security.core.authority.AuthorityUtils; 15 | import org.springframework.security.core.userdetails.UserDetails; 16 | import org.springframework.security.core.userdetails.UserDetailsService; 17 | import org.springframework.security.core.userdetails.UsernameNotFoundException; 18 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; 19 | import org.springframework.security.crypto.factory.PasswordEncoderFactories; 20 | import org.springframework.security.crypto.password.PasswordEncoder; 21 | import org.springframework.web.client.RestTemplate; 22 | 23 | import java.util.List; 24 | 25 | /** 26 | * @description Security核心配置 27 | * @author Zhifeng.Zeng 28 | */ 29 | @Configuration 30 | @EnableWebSecurity 31 | @Slf4j 32 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 33 | 34 | 35 | @Autowired 36 | private UserRepository userRepository; 37 | 38 | @Bean 39 | @Override 40 | public AuthenticationManager authenticationManagerBean() throws Exception { 41 | return super.authenticationManagerBean(); 42 | } 43 | 44 | @Bean 45 | public RestTemplate restTemplate(){ 46 | return new RestTemplate(); 47 | } 48 | 49 | @Bean 50 | @Override 51 | protected UserDetailsService userDetailsService() { 52 | BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); 53 | return new UserDetailsService(){ 54 | @Override 55 | public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 56 | log.info("username:{}",username); 57 | User user = userRepository.findUserByAccount(username); 58 | if(user != null){ 59 | CustomUserDetail customUserDetail = new CustomUserDetail(); 60 | customUserDetail.setUsername(user.getAccount()); 61 | customUserDetail.setPassword("{bcrypt}"+bCryptPasswordEncoder.encode(user.getPassword())); 62 | List list = AuthorityUtils.createAuthorityList(user.getRole().getRole()); 63 | customUserDetail.setAuthorities(list); 64 | return customUserDetail; 65 | }else {//返回空 66 | return null; 67 | } 68 | 69 | } 70 | }; 71 | } 72 | 73 | @Bean 74 | PasswordEncoder passwordEncoder() { 75 | return PasswordEncoderFactories.createDelegatingPasswordEncoder(); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/config/ServerConfig.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.config; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.boot.web.context.WebServerInitializedEvent; 5 | import org.springframework.context.ApplicationListener; 6 | import org.springframework.stereotype.Component; 7 | 8 | import java.net.InetAddress; 9 | import java.net.UnknownHostException; 10 | 11 | /** 12 | * @author Rong.Jia 13 | * @description: server 配置 14 | * @date 2019/02/19 15:24:22 15 | */ 16 | @Component 17 | public class ServerConfig implements ApplicationListener { 18 | 19 | @Value("${server.host}") 20 | private String ip; 21 | 22 | private int serverPort; 23 | 24 | public String getUrl() { 25 | InetAddress address = null; 26 | try { 27 | address = InetAddress.getLocalHost(); 28 | } catch (UnknownHostException e) { 29 | e.printStackTrace(); 30 | } 31 | return "http://" + ip + ":" + this.serverPort; 32 | } 33 | 34 | public int getPort() { 35 | return this.serverPort; 36 | } 37 | 38 | @Override 39 | public void onApplicationEvent(WebServerInitializedEvent event) { 40 | this.serverPort = event.getWebServer().getPort(); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/controller/AuthController.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.controller; 2 | 3 | import com.unionman.springbootsecurityauth2.dto.LoginUserDTO; 4 | import com.unionman.springbootsecurityauth2.dto.UserDTO; 5 | import com.unionman.springbootsecurityauth2.service.RoleService; 6 | import com.unionman.springbootsecurityauth2.service.UserService; 7 | import com.unionman.springbootsecurityauth2.utils.AssertUtils; 8 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; 12 | import org.springframework.validation.annotation.Validated; 13 | import org.springframework.web.bind.annotation.*; 14 | 15 | import javax.validation.Valid; 16 | 17 | /** 18 | * @description 用户权限管理 19 | * @author Zhifeng.Zeng 20 | * @date 2019/4/19 13:58 21 | */ 22 | @Slf4j 23 | @Validated 24 | @RestController 25 | @RequestMapping("/auth/") 26 | public class AuthController { 27 | 28 | @Autowired 29 | private UserService userService; 30 | 31 | @Autowired 32 | private RoleService roleService; 33 | 34 | @Autowired 35 | private RedisTokenStore redisTokenStore; 36 | 37 | /** 38 | * @description 添加用户 39 | * @param userDTO 40 | * @return 41 | */ 42 | @PostMapping("user") 43 | public ResponseVO add(@Valid @RequestBody UserDTO userDTO) throws Exception { 44 | userService.addUser(userDTO); 45 | return ResponseVO.success(); 46 | } 47 | 48 | /** 49 | * @description 删除用户 50 | * @param id 51 | * @return 52 | */ 53 | @DeleteMapping("user/{id}") 54 | public ResponseVO deleteUser(@PathVariable("id")Integer id) throws Exception { 55 | userService.deleteUser(id); 56 | return ResponseVO.success(); 57 | } 58 | 59 | /** 60 | * @descripiton 修改用户 61 | * @param userDTO 62 | * @return 63 | */ 64 | @PutMapping("user") 65 | public ResponseVO updateUser(@Valid @RequestBody UserDTO userDTO){ 66 | userService.updateUser(userDTO); 67 | return ResponseVO.success(); 68 | } 69 | 70 | /** 71 | * @description 获取用户列表 72 | * @return 73 | */ 74 | @GetMapping("user") 75 | public ResponseVO findAllUser(){ 76 | return userService.findAllUserVO(); 77 | } 78 | 79 | /** 80 | * @description 用户登录 81 | * @param loginUserDTO 82 | * @return 83 | */ 84 | @PostMapping("user/login") 85 | public ResponseVO login(LoginUserDTO loginUserDTO){ 86 | return userService.login(loginUserDTO); 87 | } 88 | 89 | 90 | /** 91 | * @description 用户注销 92 | * @param authorization 93 | * @return 94 | */ 95 | @GetMapping("user/logout") 96 | public ResponseVO logout(@RequestHeader("Authorization") String authorization){ 97 | redisTokenStore.removeAccessToken(AssertUtils.extracteToken(authorization)); 98 | return ResponseVO.success(); 99 | } 100 | 101 | /** 102 | * @description 获取所有角色列表 103 | * @return 104 | */ 105 | @GetMapping("role") 106 | public ResponseVO findAllRole(){ 107 | return roleService.findAllRoleVO(); 108 | } 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/domain/Base.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.domain; 2 | 3 | import lombok.Data; 4 | 5 | import javax.persistence.GeneratedValue; 6 | import javax.persistence.GenerationType; 7 | import javax.persistence.Id; 8 | import javax.persistence.MappedSuperclass; 9 | import javax.validation.constraints.NotNull; 10 | import java.io.Serializable; 11 | 12 | /** 13 | * @description: 14 | * @date 2019/02/19 18:01:22 15 | * @author Rong.Jia 16 | */ 17 | @Data 18 | @MappedSuperclass 19 | public class Base implements Serializable { 20 | 21 | private static final long serialVersionUID = -7519418012137093264L; 22 | 23 | @Id 24 | @GeneratedValue(strategy = GenerationType.IDENTITY) 25 | protected Integer id; 26 | 27 | /** 28 | * 添加时间 29 | */ 30 | protected Long createdTime; 31 | 32 | 33 | /** 34 | * 描述 35 | */ 36 | protected String description; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/domain/CustomUserDetail.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.domain; 2 | 3 | import lombok.Setter; 4 | import org.springframework.security.core.GrantedAuthority; 5 | import org.springframework.security.core.userdetails.UserDetails; 6 | 7 | import java.io.Serializable; 8 | import java.util.Collection; 9 | 10 | /** 11 | * @author Zhifeng.Zeng 12 | * @description 自定义userDetail 关联security oauth2 13 | * @date 2019/3/4 14 | */ 15 | @Setter 16 | public class CustomUserDetail implements UserDetails, Serializable { 17 | private static final long serialVersionUID = -8478114427891717226L; 18 | 19 | /** 20 | * 用户名 21 | */ 22 | private String username; 23 | /** 24 | * 密码 25 | */ 26 | private String password; 27 | /** 28 | * 账户生效 29 | */ 30 | private boolean accountNonExpired = true; 31 | /** 32 | * 账户锁定 33 | */ 34 | private boolean accountNonLocked = true; 35 | /** 36 | * 凭证生效 37 | */ 38 | private boolean credentialsNonExpired = true; 39 | /** 40 | * 激活状态 41 | */ 42 | private boolean enabled = true; 43 | /** 44 | * 权限列表 45 | */ 46 | private Collection authorities; 47 | 48 | @Override 49 | public Collection getAuthorities() { 50 | return authorities; 51 | } 52 | 53 | @Override 54 | public String getPassword() { 55 | return password; 56 | } 57 | 58 | @Override 59 | public String getUsername() { 60 | return username; 61 | } 62 | 63 | @Override 64 | public boolean isAccountNonExpired() { 65 | return accountNonExpired; 66 | } 67 | 68 | @Override 69 | public boolean isAccountNonLocked() { 70 | return accountNonLocked; 71 | } 72 | 73 | @Override 74 | public boolean isCredentialsNonExpired() { 75 | return credentialsNonExpired; 76 | } 77 | 78 | @Override 79 | public boolean isEnabled() { 80 | return enabled; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/domain/Token.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.domain; 2 | 3 | import com.unionman.springbootsecurityauth2.domain.bean.RefreshTokenBean; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author Zhifeng.Zeng 9 | * @description oauth2客户端token参数 10 | * @date 2019/3/8 11 | */ 12 | public class Token { 13 | 14 | /** 15 | * 过期时间 16 | */ 17 | private String expiration; 18 | /** 19 | * 是否过期 20 | */ 21 | private boolean expired; 22 | /** 23 | * 过期时限 24 | */ 25 | private int expiresIn; 26 | /** 27 | * refreshToken对象 28 | */ 29 | private RefreshTokenBean refreshToken; 30 | 31 | /** 32 | * token类型 33 | */ 34 | private String tokenType; 35 | 36 | /** 37 | * access_token值 38 | */ 39 | private String value; 40 | 41 | /** 42 | * 使用范围 43 | */ 44 | private List scope; 45 | 46 | 47 | public String getExpiration() { 48 | return expiration; 49 | } 50 | 51 | public void setExpiration(String expiration) { 52 | this.expiration = expiration; 53 | } 54 | 55 | public boolean isExpired() { 56 | return expired; 57 | } 58 | 59 | public void setExpired(boolean expired) { 60 | this.expired = expired; 61 | } 62 | 63 | public int getExpiresIn() { 64 | return expiresIn; 65 | } 66 | 67 | public void setExpiresIn(int expiresIn) { 68 | this.expiresIn = expiresIn; 69 | } 70 | 71 | public RefreshTokenBean getRefreshToken() { 72 | return refreshToken; 73 | } 74 | 75 | public void setRefreshToken(RefreshTokenBean refreshToken) { 76 | this.refreshToken = refreshToken; 77 | } 78 | 79 | public String getTokenType() { 80 | return tokenType; 81 | } 82 | 83 | public void setTokenType(String tokenType) { 84 | this.tokenType = tokenType; 85 | } 86 | 87 | public String getValue() { 88 | return value; 89 | } 90 | 91 | public void setValue(String value) { 92 | this.value = value; 93 | } 94 | 95 | public List getScope() { 96 | return scope; 97 | } 98 | 99 | public void setScope(List scope) { 100 | this.scope = scope; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/domain/bean/RefreshTokenBean.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.domain.bean; 2 | 3 | import lombok.Data; 4 | 5 | @Data 6 | public class RefreshTokenBean { 7 | 8 | /** 9 | * 过期时间 10 | */ 11 | private String expiration; 12 | /** 13 | * token值 14 | */ 15 | private String value; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/dto/LoginUserDTO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.dto; 2 | 3 | import lombok.Data; 4 | 5 | 6 | /** 7 | * @description 登录用户传输参数 8 | * @author Zhifeng.Zeng 9 | * @date 2019/4/19 14:26 10 | */ 11 | @Data 12 | public class LoginUserDTO { 13 | 14 | /** 15 | * 用户名 16 | */ 17 | private String account; 18 | 19 | /** 20 | * 用户密码 21 | */ 22 | private String password; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/dto/ModifyUserDTO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @description 用户修改密码传输参数 7 | * @author Zhifeng.Zeng 8 | * @date 2019/4/19 14:26 9 | */ 10 | @Data 11 | public class ModifyUserDTO { 12 | 13 | /** 14 | * 原密码 15 | */ 16 | String oldPassword; 17 | 18 | /** 19 | * 新密码 20 | */ 21 | String newPassword; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/dto/UserDTO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.dto; 2 | 3 | import com.unionman.springbootsecurityauth2.domain.Base; 4 | import lombok.Data; 5 | 6 | import javax.validation.constraints.NotNull; 7 | 8 | /** 9 | * @description 添加、修改用户传输参数 10 | * @author Zhifeng.Zeng 11 | * @date 2019/4/19 12 | */ 13 | @Data 14 | public class UserDTO extends Base { 15 | 16 | /** 17 | * 用户名 18 | */ 19 | private String account; 20 | /** 21 | * 用户姓名 22 | */ 23 | private String name; 24 | /** 25 | * 用户密码 26 | */ 27 | private String password; 28 | /** 29 | * 用户角色id 30 | */ 31 | private Integer roleId; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/entity/Role.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonBackReference; 4 | import com.unionman.springbootsecurityauth2.domain.Base; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | import java.io.Serializable; 10 | import java.util.Set; 11 | 12 | @Getter 13 | @Setter 14 | @Entity 15 | @Table(name = "um_t_role") 16 | public class Role extends Base implements Serializable { 17 | 18 | private static final long serialVersionUID = -8478114427891717226L; 19 | 20 | /** 21 | * 角色名 22 | */ 23 | private String name; 24 | 25 | /** 26 | * 角色 27 | */ 28 | private String role; 29 | 30 | /** 31 | * 角色 -- 用户: 1对多 32 | */ 33 | @JsonBackReference 34 | @OneToMany(fetch = FetchType.EAGER) 35 | @JoinTable(name = "um_t_role_user", joinColumns = {@JoinColumn(name = "roleId")}, inverseJoinColumns = {@JoinColumn(name = "userId")}) 36 | private Set users; 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/entity/User.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.entity; 2 | 3 | import com.fasterxml.jackson.annotation.JsonBackReference; 4 | import com.unionman.springbootsecurityauth2.domain.Base; 5 | import lombok.Getter; 6 | import lombok.Setter; 7 | 8 | import javax.persistence.*; 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @Setter 13 | @Entity 14 | @Table(name = "um_t_user") 15 | public class User extends Base implements Serializable { 16 | private static final long serialVersionUID = -8478114427891717226L; 17 | 18 | /** 19 | * 用户账号 20 | */ 21 | private String account; 22 | 23 | /** 24 | * 用户名 25 | */ 26 | private String name; 27 | 28 | /** 29 | * 用户密码 30 | */ 31 | private String password; 32 | 33 | /** 34 | * 用户 --角色 多对一 35 | */ 36 | @JsonBackReference 37 | @ManyToOne(fetch = FetchType.EAGER) 38 | @JoinTable(name = "um_t_role_user", joinColumns = {@JoinColumn(name = "userId")}, inverseJoinColumns = {@JoinColumn(name = "roleId")}) 39 | private Role role; 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/enums/ResponseEnum.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.enums; 2 | 3 | import lombok.ToString; 4 | 5 | /** 6 | * @description: 数据信息状态枚举类 7 | * @author Rong.Jia 8 | * @date 2019/02/19 15:54:22 9 | */ 10 | @ToString 11 | public enum ResponseEnum { 12 | 13 | 14 | 15 | /** 16 | * 0 表示返回成功 17 | */ 18 | SUCCESS(0,"成功"), 19 | 20 | /** 21 | * 表示接口调用方异常提示 22 | */ 23 | ACCESS_TOKEN_INVALID(1001,"access_token无效"), 24 | REFRESH_TOKEN_INVALID(1002,"refresh_token无效"), 25 | INSUFFICIENT_PERMISSIONS(1003,"该用户权限不足以访问该资源接口"), 26 | UNAUTHORIZED(1004,"访问此资源需要完全的身份验证"), 27 | 28 | 29 | /** 30 | * 5000 表示用户提示信息 31 | */ 32 | INCORRECT_PARAMS(5000, "参数不正确"), 33 | ; 34 | private Integer code; 35 | private String message; 36 | 37 | ResponseEnum(Integer code, String message) { 38 | this.code = code; 39 | this.message = message; 40 | } 41 | 42 | public Integer getCode() { 43 | return code; 44 | } 45 | 46 | public String getMessage() { 47 | return message; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/enums/UrlEnum.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.enums; 2 | 3 | /** 4 | * @description: url 枚举类 5 | * @author: Rong.Jia 6 | * @date: 2019/02/28 09:22:22 7 | */ 8 | public enum UrlEnum { 9 | 10 | //oauth2登录 11 | LOGIN_URL("/oauth/token"), 12 | 13 | ; 14 | 15 | private String url; 16 | 17 | UrlEnum(String url) { 18 | this.url = url; 19 | 20 | } 21 | 22 | 23 | public String getUrl() { 24 | return url; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/handler/CustomAuthExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.handler; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.unionman.springbootsecurityauth2.enums.ResponseEnum; 5 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.security.access.AccessDeniedException; 8 | import org.springframework.security.core.AuthenticationException; 9 | import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; 10 | import org.springframework.security.web.AuthenticationEntryPoint; 11 | import org.springframework.security.web.access.AccessDeniedHandler; 12 | import org.springframework.stereotype.Component; 13 | 14 | import javax.servlet.ServletException; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.io.IOException; 18 | 19 | /** 20 | * @author Zhifeng.Zeng 21 | * @description 自定义未授权 token无效 权限不足返回信息处理类 22 | * @date 2019/3/4 15:49 23 | */ 24 | @Component 25 | @Slf4j 26 | public class CustomAuthExceptionHandler implements AuthenticationEntryPoint, AccessDeniedHandler { 27 | @Override 28 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { 29 | 30 | Throwable cause = authException.getCause(); 31 | response.setContentType("application/json;charset=UTF-8"); 32 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 33 | // CORS "pre-flight" request 34 | response.addHeader("Access-Control-Allow-Origin", "*"); 35 | response.addHeader("Cache-Control","no-cache"); 36 | response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); 37 | response.setHeader("Access-Control-Allow-Headers", "x-requested-with"); 38 | response.addHeader("Access-Control-Max-Age", "1800"); 39 | if (cause instanceof InvalidTokenException) { 40 | log.error("InvalidTokenException : {}",cause.getMessage()); 41 | //Token无效 42 | response.getWriter().write(JSON.toJSONString(ResponseVO.error(ResponseEnum.ACCESS_TOKEN_INVALID))); 43 | } else { 44 | log.error("AuthenticationException : NoAuthentication"); 45 | //资源未授权 46 | response.getWriter().write(JSON.toJSONString(ResponseVO.error(ResponseEnum.UNAUTHORIZED))); 47 | } 48 | 49 | } 50 | 51 | @Override 52 | public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { 53 | response.setContentType("application/json;charset=UTF-8"); 54 | response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); 55 | response.addHeader("Access-Control-Allow-Origin", "*"); 56 | response.addHeader("Cache-Control","no-cache"); 57 | response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS"); 58 | response.setHeader("Access-Control-Allow-Headers", "x-requested-with"); 59 | response.addHeader("Access-Control-Max-Age", "1800"); 60 | //访问资源的用户权限不足 61 | log.error("AccessDeniedException : {}",accessDeniedException.getMessage()); 62 | response.getWriter().write(JSON.toJSONString(ResponseVO.error(ResponseEnum.INSUFFICIENT_PERMISSIONS))); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/repository/RoleRepository.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.repository; 2 | 3 | 4 | import com.unionman.springbootsecurityauth2.entity.Role; 5 | import com.unionman.springbootsecurityauth2.repository.base.BaseRepository; 6 | 7 | public interface RoleRepository extends BaseRepository { 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.repository; 2 | 3 | import com.unionman.springbootsecurityauth2.entity.User; 4 | import com.unionman.springbootsecurityauth2.repository.base.BaseRepository; 5 | 6 | public interface UserRepository extends BaseRepository { 7 | 8 | User findUserByAccount(String account); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/repository/base/BaseRepository.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.repository.base; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.data.jpa.repository.JpaSpecificationExecutor; 5 | import org.springframework.data.repository.NoRepositoryBean; 6 | 7 | import java.util.List; 8 | import java.util.Optional; 9 | 10 | /** 11 | * @description: 抽取持久层通用方法 12 | * @param 13 | * @author: Rong.Jia 14 | * @date: 2019/01/14 17:00 15 | */ 16 | @NoRepositoryBean 17 | public interface BaseRepository extends JpaRepository, JpaSpecificationExecutor { 18 | 19 | /** 20 | * @param id 信息唯一标识 21 | * @return T 获取的信息 22 | * @description: 根据id 获取信息 23 | * @author: Rong.Jia 24 | * @date: 2019/01/14 17:00 25 | */ 26 | @Override 27 | Optional findById(Integer id); 28 | 29 | /** 30 | * @return 将获取的信息封装到List中 返回 31 | * @description: 获取所有的信息 32 | * @author: Rong.Jia 33 | * @date: 2019/01/14 17:00 34 | */ 35 | @Override 36 | List findAll(); 37 | 38 | /** 39 | * @param entity 实体类信息 40 | * @description: 删除指定的信息 41 | * @author: Rong.Jia 42 | * @date: 2019/01/14 17:00 43 | */ 44 | @Override 45 | void delete(T entity); 46 | 47 | /** 48 | * @param id 唯一标识 49 | * @description: 根据id 删除信息 50 | * @author: Rong.Jia 51 | * @date: 2019/01/14 17:00 52 | */ 53 | @Override 54 | void deleteById(Integer id); 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/service/RoleService.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.service; 2 | 3 | 4 | import com.unionman.springbootsecurityauth2.entity.Role; 5 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 6 | 7 | /** 8 | * @description 角色管理接口 9 | * @author Zhifeng.Zeng 10 | * @date 2019/2/21 11:05 11 | */ 12 | public interface RoleService { 13 | 14 | /** 15 | * @description 获取角色列表 16 | * @return 17 | */ 18 | ResponseVO findAllRoleVO(); 19 | 20 | /** 21 | * @description 根据角色id获取角色 22 | */ 23 | Role findById(Integer id); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.service; 2 | 3 | 4 | import com.unionman.springbootsecurityauth2.dto.LoginUserDTO; 5 | import com.unionman.springbootsecurityauth2.dto.UserDTO; 6 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 7 | import com.unionman.springbootsecurityauth2.vo.UserVO; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * @description 用户业务接口 13 | * @author Zhifeng.Zeng 14 | * @date 2019/2/21 14:06 15 | */ 16 | public interface UserService { 17 | 18 | /** 19 | * @description 添加用户 20 | */ 21 | void addUser(UserDTO userDTO) throws Exception; 22 | 23 | /** 24 | * 删除用户 25 | * @param id 26 | */ 27 | void deleteUser(Integer id) throws Exception; 28 | 29 | /** 30 | * @description 修改用户信息 31 | * @param userDTO 32 | */ 33 | void updateUser(UserDTO userDTO); 34 | 35 | /** 36 | * @description 获取所有用户列表VO 37 | * @return 38 | */ 39 | ResponseVO> findAllUserVO(); 40 | 41 | /** 42 | * @description 用户登录 43 | * @return 44 | */ 45 | ResponseVO login(LoginUserDTO loginUserDTO); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/service/impl/RoleServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.service.impl; 2 | 3 | import com.unionman.springbootsecurityauth2.entity.Role; 4 | import com.unionman.springbootsecurityauth2.repository.RoleRepository; 5 | import com.unionman.springbootsecurityauth2.service.RoleService; 6 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 7 | import com.unionman.springbootsecurityauth2.vo.RoleVO; 8 | import org.springframework.beans.BeanUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @Service 16 | public class RoleServiceImpl implements RoleService { 17 | 18 | @Autowired 19 | private RoleRepository roleRepository; 20 | 21 | @Override 22 | public ResponseVO findAllRoleVO() { 23 | List rolePOList = roleRepository.findAll(); 24 | List roleVOList = new ArrayList<>(); 25 | rolePOList.forEach(rolePO->{ 26 | RoleVO roleVO = new RoleVO(); 27 | BeanUtils.copyProperties(rolePO,roleVO); 28 | roleVOList.add(roleVO); 29 | }); 30 | return ResponseVO.success(roleVOList); 31 | } 32 | 33 | @Override 34 | public Role findById(Integer id) { 35 | return roleRepository.findById(id).get(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/service/impl/UserServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.service.impl; 2 | 3 | import com.unionman.springbootsecurityauth2.config.ServerConfig; 4 | import com.unionman.springbootsecurityauth2.domain.Token; 5 | import com.unionman.springbootsecurityauth2.dto.LoginUserDTO; 6 | import com.unionman.springbootsecurityauth2.dto.UserDTO; 7 | import com.unionman.springbootsecurityauth2.entity.Role; 8 | import com.unionman.springbootsecurityauth2.entity.User; 9 | import com.unionman.springbootsecurityauth2.enums.ResponseEnum; 10 | import com.unionman.springbootsecurityauth2.enums.UrlEnum; 11 | import com.unionman.springbootsecurityauth2.repository.UserRepository; 12 | import com.unionman.springbootsecurityauth2.service.RoleService; 13 | import com.unionman.springbootsecurityauth2.service.UserService; 14 | import com.unionman.springbootsecurityauth2.utils.BeanUtils; 15 | import com.unionman.springbootsecurityauth2.utils.RedisUtil; 16 | import com.unionman.springbootsecurityauth2.vo.LoginUserVO; 17 | import com.unionman.springbootsecurityauth2.vo.ResponseVO; 18 | import com.unionman.springbootsecurityauth2.vo.RoleVO; 19 | import com.unionman.springbootsecurityauth2.vo.UserVO; 20 | import org.springframework.beans.factory.annotation.Autowired; 21 | import org.springframework.stereotype.Service; 22 | import org.springframework.transaction.annotation.Transactional; 23 | import org.springframework.util.LinkedMultiValueMap; 24 | import org.springframework.util.MultiValueMap; 25 | import org.springframework.web.client.RestClientException; 26 | import org.springframework.web.client.RestTemplate; 27 | 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.concurrent.TimeUnit; 31 | 32 | import static com.unionman.springbootsecurityauth2.config.OAuth2Config.CLIENT_ID; 33 | import static com.unionman.springbootsecurityauth2.config.OAuth2Config.CLIENT_SECRET; 34 | import static com.unionman.springbootsecurityauth2.config.OAuth2Config.GRANT_TYPE; 35 | 36 | @Service 37 | public class UserServiceImpl implements UserService { 38 | 39 | @Autowired 40 | private UserRepository userRepository; 41 | 42 | @Autowired 43 | private RoleService roleService; 44 | 45 | @Autowired 46 | private RestTemplate restTemplate; 47 | 48 | @Autowired 49 | private ServerConfig serverConfig; 50 | 51 | @Autowired 52 | private RedisUtil redisUtil; 53 | 54 | @Override 55 | @Transactional(rollbackFor = Exception.class) 56 | public void addUser(UserDTO userDTO) { 57 | User userPO = new User(); 58 | User userByAccount = userRepository.findUserByAccount(userDTO.getAccount()); 59 | if(userByAccount != null){ 60 | //此处应该用自定义异常去返回,在这里我就不去具体实现了 61 | try { 62 | throw new Exception("This user already exists!"); 63 | } catch (Exception e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | userPO.setCreatedTime(System.currentTimeMillis()); 68 | //添加用户角色信息 69 | Role rolePO = roleService.findById(userDTO.getRoleId()); 70 | userPO.setRole(rolePO); 71 | BeanUtils.copyPropertiesIgnoreNull(userDTO,userPO); 72 | userRepository.save(userPO); 73 | } 74 | 75 | @Override 76 | @Transactional(rollbackFor = Exception.class) 77 | public void deleteUser(Integer id) { 78 | User userPO = userRepository.findById(id).get(); 79 | if(userPO == null){ 80 | //此处应该用自定义异常去返回,在这里我就不去具体实现了 81 | try { 82 | throw new Exception("This user not exists!"); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | } 86 | } 87 | userRepository.delete(userPO); 88 | } 89 | 90 | @Override 91 | @Transactional(rollbackFor = Exception.class) 92 | public void updateUser(UserDTO userDTO) { 93 | User userPO = userRepository.findById(userDTO.getId()).get(); 94 | if(userPO == null){ 95 | //此处应该用自定义异常去返回,在这里我就不去具体实现了 96 | try { 97 | throw new Exception("This user not exists!"); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | } 102 | BeanUtils.copyPropertiesIgnoreNull(userDTO, userPO); 103 | //修改用户角色信息 104 | Role rolePO = roleService.findById(userDTO.getRoleId()); 105 | userPO.setRole(rolePO); 106 | userRepository.saveAndFlush(userPO); 107 | } 108 | 109 | @Override 110 | public ResponseVO> findAllUserVO() { 111 | List userPOList = userRepository.findAll(); 112 | List userVOList = new ArrayList<>(); 113 | userPOList.forEach(userPO->{ 114 | UserVO userVO = new UserVO(); 115 | BeanUtils.copyPropertiesIgnoreNull(userPO,userVO); 116 | RoleVO roleVO = new RoleVO(); 117 | BeanUtils.copyPropertiesIgnoreNull(userPO.getRole(),roleVO); 118 | userVO.setRole(roleVO); 119 | userVOList.add(userVO); 120 | }); 121 | return ResponseVO.success(userVOList); 122 | } 123 | 124 | @Override 125 | public ResponseVO login(LoginUserDTO loginUserDTO) { 126 | MultiValueMap paramMap = new LinkedMultiValueMap<>(); 127 | paramMap.add("client_id", CLIENT_ID); 128 | paramMap.add("client_secret", CLIENT_SECRET); 129 | paramMap.add("username", loginUserDTO.getAccount()); 130 | paramMap.add("password", loginUserDTO.getPassword()); 131 | paramMap.add("grant_type", GRANT_TYPE[0]); 132 | Token token = null; 133 | try { 134 | //因为oauth2本身自带的登录接口是"/oauth/token",并且返回的数据类型不能按我们想要的去返回 135 | //但是我的业务需求是,登录接口是"user/login",由于我没研究过要怎么去修改oauth2内部的endpoint配置 136 | //所以这里我用restTemplate(HTTP客户端)进行一次转发到oauth2内部的登录接口,比较简单粗暴 137 | token = restTemplate.postForObject(serverConfig.getUrl() + UrlEnum.LOGIN_URL.getUrl(), paramMap, Token.class); 138 | LoginUserVO loginUserVO = redisUtil.get(token.getValue(), LoginUserVO.class); 139 | if(loginUserVO != null){ 140 | //登录的时候,判断该用户是否已经登录过了 141 | //如果redis里面已经存在该用户已经登录过了的信息 142 | //我这边要刷新一遍token信息,不然,它会返回上一次还未过时的token信息给你 143 | //不便于做单点维护 144 | token = oauthRefreshToken(loginUserVO.getRefreshToken()); 145 | redisUtil.deleteCache(loginUserVO.getAccessToken()); 146 | } 147 | } catch (RestClientException e) { 148 | try { 149 | e.printStackTrace(); 150 | //此处应该用自定义异常去返回,在这里我就不去具体实现了 151 | //throw new Exception("username or password error"); 152 | } catch (Exception e1) { 153 | e1.printStackTrace(); 154 | } 155 | } 156 | //这里我拿到了登录成功后返回的token信息之后,我再进行一层封装,最后返回给前端的其实是LoginUserVO 157 | LoginUserVO loginUserVO = new LoginUserVO(); 158 | User userPO = userRepository.findUserByAccount(loginUserDTO.getAccount()); 159 | BeanUtils.copyPropertiesIgnoreNull(userPO, loginUserVO); 160 | loginUserVO.setPassword(userPO.getPassword()); 161 | loginUserVO.setAccessToken(token.getValue()); 162 | loginUserVO.setAccessTokenExpiresIn(token.getExpiresIn()); 163 | loginUserVO.setAccessTokenExpiration(token.getExpiration()); 164 | loginUserVO.setExpired(token.isExpired()); 165 | loginUserVO.setScope(token.getScope()); 166 | loginUserVO.setTokenType(token.getTokenType()); 167 | loginUserVO.setRefreshToken(token.getRefreshToken().getValue()); 168 | loginUserVO.setRefreshTokenExpiration(token.getRefreshToken().getExpiration()); 169 | //存储登录的用户 170 | redisUtil.set(loginUserVO.getAccessToken(),loginUserVO,TimeUnit.HOURS.toSeconds(1)); 171 | return ResponseVO.success(loginUserVO); 172 | } 173 | 174 | /** 175 | * @description oauth2客户端刷新token 176 | * @param refreshToken 177 | * @date 2019/03/05 14:27:22 178 | * @author Zhifeng.Zeng 179 | * @return 180 | */ 181 | private Token oauthRefreshToken(String refreshToken) { 182 | MultiValueMap paramMap = new LinkedMultiValueMap<>(); 183 | paramMap.add("client_id", CLIENT_ID); 184 | paramMap.add("client_secret", CLIENT_SECRET); 185 | paramMap.add("refresh_token", refreshToken); 186 | paramMap.add("grant_type", GRANT_TYPE[1]); 187 | Token token = null; 188 | try { 189 | token = restTemplate.postForObject(serverConfig.getUrl() + UrlEnum.LOGIN_URL.getUrl(), paramMap, Token.class); 190 | } catch (RestClientException e) { 191 | try { 192 | //此处应该用自定义异常去返回,在这里我就不去具体实现了 193 | throw new Exception(ResponseEnum.REFRESH_TOKEN_INVALID.getMessage()); 194 | } catch (Exception e1) { 195 | e1.printStackTrace(); 196 | } 197 | } 198 | return token; 199 | } 200 | 201 | 202 | } 203 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/utils/AssertUtils.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.utils; 2 | 3 | public class AssertUtils { 4 | /** 5 | * @description 获取请求头的token 6 | * @param authToken 7 | * @return 8 | */ 9 | public static String extracteToken(String authToken){ 10 | String authTokenPrefix="Bearer"; 11 | if(authToken.indexOf(authTokenPrefix)!=-1){ 12 | return authToken.substring(7); 13 | }else { 14 | return authToken; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/utils/BeanUtils.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.BeanWrapper; 5 | import org.springframework.beans.BeanWrapperImpl; 6 | import org.springframework.util.LinkedMultiValueMap; 7 | import org.springframework.util.MultiValueMap; 8 | 9 | import java.beans.BeanInfo; 10 | import java.beans.IntrospectionException; 11 | import java.beans.Introspector; 12 | import java.beans.PropertyDescriptor; 13 | import java.lang.reflect.Constructor; 14 | import java.lang.reflect.InvocationTargetException; 15 | import java.lang.reflect.Method; 16 | import java.util.*; 17 | 18 | /** 19 | * @description: bean工具类 20 | * @date 2019/02/21 09:22 21 | * @author Zhifeng.Zeng 22 | */ 23 | @Slf4j 24 | public class BeanUtils { 25 | 26 | /** 27 | * 属性拷贝方法,忽略入参值为空 28 | * @author Zhifeng.Zeng 29 | * @param src 30 | * @param target 31 | */ 32 | public static void copyPropertiesIgnoreNull(Object src, Object target){ 33 | org.springframework.beans.BeanUtils.copyProperties(src, target, getNullPropertyNames(src)); 34 | } 35 | 36 | private static String[] getNullPropertyNames (Object source) { 37 | final BeanWrapper src = new BeanWrapperImpl(source); 38 | PropertyDescriptor[] pds = src.getPropertyDescriptors(); 39 | 40 | Set emptyNames = new HashSet(); 41 | for(PropertyDescriptor pd : pds) { 42 | Object srcValue = src.getPropertyValue(pd.getName()); 43 | if (srcValue == null) { 44 | emptyNames.add(pd.getName()); 45 | } 46 | } 47 | String[] result = new String[emptyNames.size()]; 48 | return emptyNames.toArray(result); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/utils/RedisUtil.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.utils; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.util.CollectionUtils; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.Set; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * @description: redis 工具类 18 | * @author: Rong.Jia 19 | * @date: 2018年6月13日 22:58:22 20 | */ 21 | @Slf4j 22 | @Component 23 | public class RedisUtil { 24 | 25 | @Autowired 26 | private RedisTemplate redisTemplate; 27 | 28 | 29 | /** 30 | * @param key 键 31 | * @param time 时间(秒) 32 | * @return boolean 33 | * @description: 指定缓存失效时间 34 | */ 35 | public boolean expire(String key, long time) { 36 | try { 37 | if (time > 0) { 38 | redisTemplate.expire(key, time, TimeUnit.SECONDS); 39 | } 40 | return true; 41 | } catch (Exception e) { 42 | log.error("class:RedisUtils method:expire() => Exception {}",e.getMessage()); 43 | } 44 | return false; 45 | } 46 | 47 | /** 48 | * @param key 键 不能为null 49 | * @return 时间(秒) 返回0代表为永久有效 50 | * @description: 根据key 获取过期时间 51 | */ 52 | public long getExpire(String key) { 53 | return redisTemplate.getExpire(key, TimeUnit.SECONDS); 54 | } 55 | 56 | /** 57 | * @param key 键 58 | * @return true 存在 false不存在 59 | * @description: 判断key是否存在 60 | */ 61 | public boolean exists(String key) { 62 | try { 63 | return redisTemplate.hasKey(key); 64 | } catch (Exception e) { 65 | log.error("class:RedisUtils method:exists() => Exception {}",e.getMessage()); 66 | } 67 | return false; 68 | } 69 | 70 | /** 71 | * @param key 可以传一个值 或多个 72 | * @description: 删除缓存 73 | */ 74 | @SuppressWarnings("unchecked") 75 | public void deleteCache(String... key) { 76 | if (key != null && key.length > 0) { 77 | if (key.length == 1) { 78 | redisTemplate.delete(key[0]); 79 | } else { 80 | redisTemplate.delete(CollectionUtils.arrayToList(key)); 81 | } 82 | } 83 | } 84 | 85 | public T get(String key, Class clazz, long expire) { 86 | 87 | Object value = redisTemplate.opsForValue().get(key); 88 | if (expire != -1L) { 89 | redisTemplate.expire(key, expire, TimeUnit.SECONDS); 90 | } 91 | return value == null ? null : fromJson(value, clazz); 92 | } 93 | 94 | public T get(String key, Class clazz) { 95 | 96 | return get(key, clazz, -1L); 97 | } 98 | 99 | /** 100 | * @param key 键 101 | * @return Object value 102 | * @description: 根据key获取value 103 | */ 104 | public Object getValueByKey(String key) { 105 | return key == null ? null : redisTemplate.opsForValue().get(key); 106 | } 107 | 108 | /** 109 | * @param key 键 110 | * @param value 值 111 | * @param expire 时间(秒) time要大于0 如果expire小于等于0 将设置无限期 112 | * @return true成功 false失败 113 | * @description: 普通缓存放入 114 | */ 115 | public void set(String key, Object value, long expire) { 116 | redisTemplate.opsForValue().set(key, value); 117 | if (expire != -1L) { 118 | redisTemplate.expire(key, expire, TimeUnit.SECONDS); 119 | } 120 | } 121 | 122 | /** 123 | * 普通缓存放入并设置时间 124 | * 125 | * @param key 键 126 | * @param value 值 127 | * @return true成功 false 失败 128 | */ 129 | public void set(String key, Object value) { 130 | set(key, value, 3600L); 131 | } 132 | 133 | /** 134 | * 递增 135 | * 136 | * @param key 键 137 | * @param delta 要增加几(大于0) 138 | * @return 139 | */ 140 | public long increasing(String key, long delta) { 141 | if (delta < 0) { 142 | log.error("class:RedisUtils method:increasing() => delta:{} delta must more than 0",delta); 143 | } 144 | return redisTemplate.opsForValue().increment(key, delta); 145 | } 146 | 147 | /** 148 | * 递减 149 | * 150 | * @param key 键 151 | * @param delta 要减少几(小于0) 152 | * @return 153 | */ 154 | public long decr(String key, long delta) { 155 | if (delta < 0) { 156 | log.error("class:RedisUtils method:decr() => delta:{} delta must more than 0",delta); 157 | } 158 | return redisTemplate.opsForValue().increment(key, -delta); 159 | } 160 | 161 | //================================Map================================= 162 | 163 | /** 164 | * HashGet 165 | * 166 | * @param key 键 不能为null 167 | * @param item 项 不能为null 168 | * @return 值 169 | */ 170 | public Object hget(String key, String item) { 171 | return redisTemplate.opsForHash().get(key, item); 172 | } 173 | 174 | /** 175 | * 获取hashKey对应的所有键值 176 | * 177 | * @param key 键 178 | * @return 对应的多个键值 179 | */ 180 | public Map hmget(String key) { 181 | return redisTemplate.opsForHash().entries(key); 182 | } 183 | 184 | /** 185 | * HashSet 186 | * 187 | * @param key 键 188 | * @param map 对应多个键值 189 | * @return true 成功 false 失败 190 | */ 191 | public boolean hmset(String key, Map map) { 192 | try { 193 | redisTemplate.opsForHash().putAll(key, map); 194 | return true; 195 | } catch (Exception e) { 196 | log.error("class:RedisUtils method:hmset() => Exception {}",e.getMessage()); 197 | } 198 | return false; 199 | } 200 | 201 | /** 202 | * HashSet 并设置时间 203 | * 204 | * @param key 键 205 | * @param map 对应多个键值 206 | * @param time 时间(秒) 207 | * @return true成功 false失败 208 | */ 209 | public boolean hmset(String key, Map map, long time) { 210 | try { 211 | redisTemplate.opsForHash().putAll(key, map); 212 | if (time > 0) { 213 | expire(key, time); 214 | } 215 | return true; 216 | } catch (Exception e) { 217 | log.error("class:RedisUtils method:hmset() => Exception {}",e.getMessage()); 218 | } 219 | return false; 220 | } 221 | 222 | /** 223 | * 向一张hash表中放入数据,如果不存在将创建 224 | * 225 | * @param key 键 226 | * @param item 项 227 | * @param value 值 228 | * @return true 成功 false失败 229 | */ 230 | public boolean hset(String key, String item, Object value) { 231 | try { 232 | redisTemplate.opsForHash().put(key, item, value); 233 | return true; 234 | } catch (Exception e) { 235 | log.error("class:RedisUtils method:hset() => Exception {}",e.getMessage()); 236 | } 237 | return false; 238 | } 239 | 240 | /** 241 | * 向一张hash表中放入数据,如果不存在将创建 242 | * 243 | * @param key 键 244 | * @param item 项 245 | * @param value 值 246 | * @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间 247 | * @return true 成功 false失败 248 | */ 249 | public boolean hset(String key, String item, Object value, long time) { 250 | try { 251 | redisTemplate.opsForHash().put(key, item, value); 252 | if (time > 0) { 253 | expire(key, time); 254 | } 255 | return true; 256 | } catch (Exception e) { 257 | log.error("class:RedisUtils method:hset() => Exception {}",e.getMessage()); 258 | } 259 | return false; 260 | } 261 | 262 | /** 263 | * 删除hash表中的值 264 | * 265 | * @param key 键 不能为null 266 | * @param item 项 可以使多个 不能为null 267 | */ 268 | public void deleteValueByKey(String key, Object... item) { 269 | redisTemplate.opsForHash().delete(key, item); 270 | } 271 | 272 | /** 273 | * 判断hash表中是否有该项的值 274 | * 275 | * @param key 键 不能为null 276 | * @param item 项 不能为null 277 | * @return true 存在 false不存在 278 | */ 279 | public boolean hHasKey(String key, String item) { 280 | return redisTemplate.opsForHash().hasKey(key, item); 281 | } 282 | 283 | /** 284 | * hash递增 如果不存在,就会创建一个 并把新增后的值返回 285 | * 286 | * @param key 键 287 | * @param item 项 288 | * @param by 要增加几(大于0) 289 | * @return 290 | */ 291 | public double hincr(String key, String item, double by) { 292 | return redisTemplate.opsForHash().increment(key, item, by); 293 | } 294 | 295 | /** 296 | * hash递减 297 | * 298 | * @param key 键 299 | * @param item 项 300 | * @param by 要减少记(小于0) 301 | * @return 302 | */ 303 | public double hdecr(String key, String item, double by) { 304 | return redisTemplate.opsForHash().increment(key, item, -by); 305 | } 306 | 307 | //============================set============================= 308 | 309 | /** 310 | * 根据key获取Set中的所有值 311 | * 312 | * @param key 键 313 | * @return 314 | */ 315 | public Set sGet(String key) { 316 | try { 317 | return redisTemplate.opsForSet().members(key); 318 | } catch (Exception e) { 319 | log.error("class:RedisUtils method:sGet() => Exception {}",e.getMessage()); 320 | } 321 | return null; 322 | } 323 | 324 | /** 325 | * 根据value从一个set中查询,是否存在 326 | * 327 | * @param key 键 328 | * @param value 值 329 | * @return true 存在 false不存在 330 | */ 331 | public boolean sHasKey(String key, Object value) { 332 | try { 333 | return redisTemplate.opsForSet().isMember(key, value); 334 | } catch (Exception e) { 335 | log.error("class:RedisUtils method:sHasKey() => Exception {}",e.getMessage()); 336 | } 337 | return false; 338 | } 339 | 340 | /** 341 | * 将数据放入set缓存 342 | * 343 | * @param key 键 344 | * @param values 值 可以是多个 345 | * @return 成功个数 346 | */ 347 | public long sSet(String key, Object... values) { 348 | try { 349 | return redisTemplate.opsForSet().add(key, values); 350 | } catch (Exception e) { 351 | log.error("class:RedisUtils method:sSet() => Exception {}",e.getMessage()); 352 | } 353 | return 0L; 354 | } 355 | 356 | /** 357 | * 将set数据放入缓存 358 | * 359 | * @param key 键 360 | * @param time 时间(秒) 361 | * @param values 值 可以是多个 362 | * @return 成功个数 363 | */ 364 | public long sSetAndTime(String key, long time, Object... values) { 365 | try { 366 | Long count = redisTemplate.opsForSet().add(key, values); 367 | if (time > 0) { 368 | expire(key, time); 369 | } 370 | return count; 371 | } catch (Exception e) { 372 | log.error("class:RedisUtils method:sSetAndTime() => Exception {}",e.getMessage()); 373 | } 374 | return 0L; 375 | } 376 | 377 | /** 378 | * 获取set缓存的长度 379 | * 380 | * @param key 键 381 | * @return 382 | */ 383 | public long sGetSetSize(String key) { 384 | try { 385 | return redisTemplate.opsForSet().size(key); 386 | } catch (Exception e) { 387 | log.error("class:RedisUtils method:sGetSetSize() => Exception {}",e.getMessage()); 388 | } 389 | return 0L; 390 | } 391 | 392 | /** 393 | * 移除值为value的 394 | * 395 | * @param key 键 396 | * @param values 值 可以是多个 397 | * @return 移除的个数 398 | */ 399 | public long setRemove(String key, Object... values) { 400 | try { 401 | Long count = redisTemplate.opsForSet().remove(key, values); 402 | return count; 403 | } catch (Exception e) { 404 | log.error("class:RedisUtils method:setRemove() => Exception {}",e.getMessage()); 405 | } 406 | return 0L; 407 | } 408 | //===============================list================================= 409 | 410 | /** 411 | * 获取list缓存的内容 412 | * 413 | * @param key 键 414 | * @param start 开始 415 | * @param end 结束 0 到 -1代表所有值 416 | * @return 417 | */ 418 | public List lGet(String key, long start, long end) { 419 | try { 420 | return redisTemplate.opsForList().range(key, start, end); 421 | } catch (Exception e) { 422 | log.error("class:RedisUtils method:lGet() => Exception {}",e.getMessage()); 423 | } 424 | return null; 425 | } 426 | 427 | /** 428 | * 获取list缓存的长度 429 | * 430 | * @param key 键 431 | * @return 432 | */ 433 | public long lGetListSize(String key) { 434 | try { 435 | return redisTemplate.opsForList().size(key); 436 | } catch (Exception e) { 437 | log.error("class:RedisUtils method:lGetListSize() => Exception {}",e.getMessage()); 438 | } 439 | return 0L; 440 | } 441 | 442 | /** 443 | * 通过索引 获取list中的值 444 | * 445 | * @param key 键 446 | * @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推 447 | * @return 448 | */ 449 | public Object lGetIndex(String key, long index) { 450 | try { 451 | return redisTemplate.opsForList().index(key, index); 452 | } catch (Exception e) { 453 | log.error("class:RedisUtils method:lGetIndex() => Exception {}",e.getMessage()); 454 | } 455 | return null; 456 | } 457 | 458 | /** 459 | * 将list放入缓存 460 | * 461 | * @param key 键 462 | * @param value 值 463 | * @return 464 | */ 465 | public boolean lSet(String key, Object value) { 466 | try { 467 | redisTemplate.opsForList().rightPush(key, value); 468 | return true; 469 | } catch (Exception e) { 470 | log.error("class:RedisUtils method:lSet() => Exception {}",e.getMessage()); 471 | } 472 | return false; 473 | } 474 | 475 | /** 476 | * 将list放入缓存 477 | * 478 | * @param key 键 479 | * @param value 值 480 | * @param time 时间(秒) 481 | * @return 482 | */ 483 | public boolean lSet(String key, Object value, long time) { 484 | try { 485 | redisTemplate.opsForList().rightPush(key, value); 486 | if (time > 0) { 487 | expire(key, time); 488 | } 489 | return true; 490 | } catch (Exception e) { 491 | log.error("class:RedisUtils method:lSet() => Exception {}",e.getMessage()); 492 | } 493 | return false; 494 | } 495 | 496 | /** 497 | * 将list放入缓存 498 | * 499 | * @param key 键 500 | * @param value 值 501 | * @return 502 | */ 503 | public boolean lSet(String key, List value) { 504 | try { 505 | redisTemplate.opsForList().rightPushAll(key, value); 506 | return true; 507 | } catch (Exception e) { 508 | log.error("class:RedisUtils method:lSet() => Exception {}",e.getMessage()); 509 | } 510 | return false; 511 | } 512 | 513 | /** 514 | * 将list放入缓存 515 | * 516 | * @param key 键 517 | * @param value 值 518 | * @param time 时间(秒) 519 | * @return 520 | */ 521 | public boolean lSet(String key, List value, long time) { 522 | try { 523 | redisTemplate.opsForList().rightPushAll(key, value); 524 | if (time > 0) { 525 | expire(key, time); 526 | } 527 | return true; 528 | } catch (Exception e) { 529 | log.error("class:RedisUtils method:lSet() => Exception {}",e.getMessage()); 530 | } 531 | return false; 532 | } 533 | 534 | /** 535 | * 根据索引修改list中的某条数据 536 | * 537 | * @param key 键 538 | * @param index 索引 539 | * @param value 值 540 | * @return 541 | */ 542 | public boolean lUpdateIndex(String key, long index, Object value) { 543 | try { 544 | redisTemplate.opsForList().set(key, index, value); 545 | return true; 546 | } catch (Exception e) { 547 | log.error("class:RedisUtils method:lUpdateIndex() => Exception {}",e.getMessage()); 548 | } 549 | return false; 550 | } 551 | 552 | /** 553 | * 移除N个值为value 554 | * 555 | * @param key 键 556 | * @param count 移除多少个 557 | * @param value 值 558 | * @return 移除的个数 559 | */ 560 | public long lRemove(String key, long count, Object value) { 561 | try { 562 | Long remove = redisTemplate.opsForList().remove(key, count, value); 563 | return remove; 564 | } catch (Exception e) { 565 | log.error("class:RedisUtils method:lRemove() => Exception {}",e.getMessage()); 566 | } 567 | return 0L; 568 | } 569 | 570 | /** 571 | * JSON数据,转成Object 572 | */ 573 | private T fromJson(Object json, Class clazz) { 574 | 575 | return JSONObject.parseObject(JSON.toJSONString(json), clazz); 576 | } 577 | } -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/vo/LoginUserVO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.vo; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import lombok.ToString; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @description 用户登录后返回参数对象 11 | * @author Zhifeng.Zeng 12 | * @date 2019/3/7 13 | */ 14 | @Setter 15 | @Getter 16 | @ToString 17 | public class LoginUserVO { 18 | 19 | /** 20 | * 用户id 21 | */ 22 | private Integer id; 23 | 24 | /** 25 | * 用户账号 26 | */ 27 | private String account; 28 | 29 | /** 30 | * 用户名 31 | */ 32 | private String name; 33 | 34 | /** 35 | * 用户密码 36 | */ 37 | private String password; 38 | 39 | /** 40 | * accessToken码 41 | */ 42 | private String accessToken; 43 | 44 | /** 45 | * accessToken是否过期 46 | */ 47 | private Boolean expired; 48 | 49 | /** 50 | * accessToken到期时间 51 | */ 52 | private String accessTokenExpiration; 53 | 54 | /** 55 | * accessToken过期时限 56 | */ 57 | private Integer accessTokenExpiresIn; 58 | 59 | /** 60 | * 使用范围 61 | */ 62 | private List scope; 63 | 64 | /** 65 | * token类型 66 | */ 67 | private String tokenType; 68 | 69 | /** 70 | * refreshToken到期时间 71 | */ 72 | private String refreshTokenExpiration; 73 | 74 | 75 | /** 76 | * refreshToken码 77 | */ 78 | private String refreshToken; 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/vo/ResponseVO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.vo; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.unionman.springbootsecurityauth2.enums.ResponseEnum; 5 | import lombok.Data; 6 | import org.springframework.context.MessageSource; 7 | import org.springframework.context.i18n.LocaleContextHolder; 8 | import org.springframework.validation.BindingResult; 9 | import org.springframework.validation.FieldError; 10 | 11 | import java.io.Serializable; 12 | import java.util.List; 13 | import java.util.Locale; 14 | 15 | /** 16 | * @description: 数据格式返回统一 17 | * @author Zhifeng.Zeng 18 | * @date 2019/02/19 16:00:22 19 | */ 20 | @Data 21 | @JsonInclude(JsonInclude.Include.NON_NULL) 22 | public class ResponseVO implements Serializable { 23 | 24 | private static final long serialVersionUID = -437839076132402939L; 25 | 26 | /** 27 | * 异常码 28 | */ 29 | private Integer code; 30 | 31 | /** 32 | * 描述 33 | */ 34 | private String message; 35 | 36 | /** 37 | * 数据 38 | */ 39 | private T data; 40 | 41 | public ResponseVO() {} 42 | 43 | public ResponseVO(Integer code, String msg) { 44 | this.code = code; 45 | this.message = msg; 46 | } 47 | 48 | public ResponseVO(Integer code, String msg, T data) { 49 | this.code = code; 50 | this.message = msg; 51 | this.data = data; 52 | } 53 | 54 | public ResponseVO(ResponseEnum responseEnum) { 55 | this.code = responseEnum.getCode(); 56 | this.message = responseEnum.getMessage(); 57 | } 58 | 59 | public ResponseVO(ResponseEnum responseEnum, T data) { 60 | this.code = responseEnum.getCode(); 61 | this.message = responseEnum.getMessage(); 62 | this.data = data; 63 | } 64 | 65 | public static ResponseVO success(){ 66 | return new ResponseVO(ResponseEnum.SUCCESS); 67 | } 68 | 69 | public static ResponseVO success(T data){ 70 | return new ResponseVO(ResponseEnum.SUCCESS, data); 71 | } 72 | 73 | public static ResponseVO success(int code, String msg){ 74 | return new ResponseVO(code, msg); 75 | } 76 | 77 | public static ResponseVO error(int code, String msg){ 78 | return new ResponseVO(code,msg); 79 | } 80 | 81 | public static ResponseVO error(ResponseEnum responseEnum){ 82 | return new ResponseVO(responseEnum); 83 | } 84 | 85 | public static ResponseVO error(ResponseEnum responseEnum, Object data){ 86 | return new ResponseVO(responseEnum, data); 87 | } 88 | 89 | public static ResponseVO errorParams(String msg){ 90 | return new ResponseVO(ResponseEnum.INCORRECT_PARAMS.getCode(), msg); 91 | } 92 | 93 | public static ResponseVO error(BindingResult result, MessageSource messageSource) { 94 | StringBuffer msg = new StringBuffer(); 95 | //获取错误字段集合 96 | List fieldErrors = result.getFieldErrors(); 97 | //获取本地locale,zh_CN 98 | Locale currentLocale = LocaleContextHolder.getLocale(); 99 | //遍历错误字段获取错误消息 100 | for (FieldError fieldError : fieldErrors) { 101 | //获取错误信息 102 | String errorMessage = messageSource.getMessage(fieldError, currentLocale); 103 | //添加到错误消息集合内 104 | msg.append(fieldError.getField() + ":" + errorMessage + " , "); 105 | } 106 | return ResponseVO.error(ResponseEnum.INCORRECT_PARAMS, msg.toString()); 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/vo/RoleVO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.vo; 2 | 3 | import com.unionman.springbootsecurityauth2.domain.Base; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | /** 9 | * @description 角色返回参数对象 10 | * @author Zhifeng.Zeng 11 | * @date 2019/3/7 12 | */ 13 | @Setter 14 | @Getter 15 | @ToString 16 | public class RoleVO extends Base { 17 | 18 | 19 | 20 | /** 21 | * 角色名(中文) 22 | */ 23 | private String name; 24 | 25 | /** 26 | * 角色名 27 | */ 28 | private String role; 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/unionman/springbootsecurityauth2/vo/UserVO.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2.vo; 2 | 3 | import com.unionman.springbootsecurityauth2.domain.Base; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | import lombok.ToString; 7 | 8 | 9 | /** 10 | * @description 用户返回参数对象 11 | * @author Zhifeng.Zeng 12 | * @date 2019/3/7 13 | */ 14 | @Setter 15 | @Getter 16 | @ToString 17 | public class UserVO extends Base { 18 | 19 | /** 20 | * 用户账号 21 | */ 22 | private String account; 23 | 24 | /** 25 | * 用户名 26 | */ 27 | private String name; 28 | 29 | /** 30 | * 用户密码 31 | */ 32 | private String password; 33 | 34 | /** 35 | * 用户角色 36 | */ 37 | private RoleVO role; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 8080 3 | host: localhost 4 | spring: 5 | # mysql 配置 6 | datasource: 7 | url: jdbc:mysql://localhost:3306/auth_test?useUnicode=true&characterEncoding=UTF-8&useSSL=false 8 | username: root 9 | password: cai19951114 10 | schema: classpath:springbootsecurityauth.sql 11 | sql-script-encoding: utf-8 12 | initialization-mode: always 13 | driver-class-name: com.mysql.jdbc.Driver 14 | # 初始化大小,最小,最大 15 | initialSize: 1 16 | minIdle: 3 17 | maxActive: 20 18 | # 配置获取连接等待超时的时间 19 | maxWait: 60000 20 | # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 21 | timeBetweenEvictionRunsMillis: 60000 22 | # 配置一个连接在池中最小生存的时间,单位是毫秒 23 | minEvictableIdleTimeMillis: 30000 24 | validationQuery: select 'x' 25 | testWhileIdle: true 26 | testOnBorrow: false 27 | testOnReturn: false 28 | # 打开PSCache,并且指定每个连接上PSCache的大小 29 | poolPreparedStatements: true 30 | maxPoolPreparedStatementPerConnectionSize: 20 31 | # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 32 | filters: stat,wall,slf4j 33 | # 通过connectProperties属性来打开mergeSql功能;慢SQL记录 34 | connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 35 | redis: 36 | database: 6 37 | host: 192.168.135.63 38 | port: 6379 39 | timeout: 5000s # 连接超时时长(毫秒) 40 | jedis: 41 | pool: 42 | max-active: 8 #连接池最大连接数(使用负值表示没有限制) 43 | max-idle: 8 #连接池中的最大空闲连接 44 | max-wait: -1s #连接池最大阻塞等待时间(使用负值表示没有限制) 45 | min-idle: 0 #连接池中的最小空闲连接 46 | 47 | # jpa 配置 48 | jpa: 49 | database: mysql 50 | show-sql: false 51 | hibernate: 52 | ddl-auto: update 53 | properties: 54 | hibernate: 55 | dialect: org.hibernate.dialect.MySQL5Dialect 56 | -------------------------------------------------------------------------------- /src/main/resources/springbootsecurityauth.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8; 2 | SET FOREIGN_KEY_CHECKS = 0; 3 | /** 4 | 初始化角色信息 5 | */ 6 | CREATE TABLE IF NOT EXISTS `um_t_role`( 7 | `id` INT(11) PRIMARY KEY AUTO_INCREMENT , 8 | `description` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 9 | `created_time` BIGINT(20) NOT NULL, 10 | `name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, 11 | `role` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL 12 | ); 13 | INSERT IGNORE INTO `um_t_role`(id,`name`,description,created_time,role) VALUES(1,'管理员','管理员拥有所有接口操作权限',UNIX_TIMESTAMP(NOW()),'ADMIN'),(2,'普通用户','普通拥有查看用户列表与修改密码权限,不具备对用户增删改权限',UNIX_TIMESTAMP(NOW()),'USER'); 14 | 15 | /** 16 | 初始化一个默认管理员 17 | */ 18 | CREATE TABLE IF NOT EXISTS `um_t_user`( 19 | `id` INT(11) PRIMARY KEY AUTO_INCREMENT , 20 | `account` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 21 | `description` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 22 | `password` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, 23 | `name` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL 24 | ); 25 | INSERT IGNORE INTO `um_t_user`(id,account,`password`,`name`,description) VALUES(1,'admin','123456','小小丰','系统默认管理员'); 26 | 27 | /** 28 | 关联表赋值 29 | */ 30 | CREATE TABLE IF NOT EXISTS `um_t_role_user`( 31 | `role_id` INT(11), 32 | `user_id` INT(11) 33 | ); 34 | INSERT IGNORE INTO `um_t_role_user`(role_id,user_id)VALUES(1,1); -------------------------------------------------------------------------------- /src/test/java/com/unionman/springbootsecurityauth2/SpringbootSecurityAuth2ApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.unionman.springbootsecurityauth2; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class SpringbootSecurityAuth2ApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | } 15 | 16 | } 17 | --------------------------------------------------------------------------------