17 changed files with 651 additions and 3 deletions
-
5pom.xml
-
11src/main/java/com/iflytop/nuclear/NuclearApplication.java
-
68src/main/java/com/iflytop/nuclear/config/SecurityConfig.java
-
56src/main/java/com/iflytop/nuclear/controller/AccountController.java
-
80src/main/java/com/iflytop/nuclear/entity/JwtUser.java
-
25src/main/java/com/iflytop/nuclear/exception/JWTAccessDeniedHandler.java
-
28src/main/java/com/iflytop/nuclear/exception/JWTAuthenticationEntryPoint.java
-
27src/main/java/com/iflytop/nuclear/exception/TokenIsExpiredException.java
-
77src/main/java/com/iflytop/nuclear/filter/JWTAuthenticationFilter.java
-
73src/main/java/com/iflytop/nuclear/filter/JWTAuthorizationFilter.java
-
13src/main/java/com/iflytop/nuclear/mapper/AccountMapper.java
-
26src/main/java/com/iflytop/nuclear/model/Account.java
-
16src/main/java/com/iflytop/nuclear/service/AccountService.java
-
28src/main/java/com/iflytop/nuclear/service/UserDetailsServiceImpl.java
-
51src/main/java/com/iflytop/nuclear/service/impl/AccountServiceImpl.java
-
68src/main/java/com/iflytop/nuclear/utils/JwtTokenUtils.java
-
2src/main/resources/application-dev.yml
@ -1,15 +1,22 @@ |
|||||
package com.iflytop.nuclear; |
package com.iflytop.nuclear; |
||||
|
|
||||
import org.mybatis.spring.annotation.MapperScan; |
import org.mybatis.spring.annotation.MapperScan; |
||||
|
import org.springframework.boot.Banner; |
||||
|
import org.springframework.boot.ResourceBanner; |
||||
import org.springframework.boot.SpringApplication; |
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
|
import org.springframework.boot.builder.SpringApplicationBuilder; |
||||
|
import org.springframework.core.io.ClassPathResource; |
||||
|
|
||||
@SpringBootApplication |
@SpringBootApplication |
||||
@MapperScan("com.iflytop.boditech.mapper") |
|
||||
|
@MapperScan("com.iflytop.nuclear.mapper") |
||||
public class NuclearApplication { |
public class NuclearApplication { |
||||
|
|
||||
public static void main(String[] args) { |
public static void main(String[] args) { |
||||
SpringApplication.run(NuclearApplication.class, args); |
|
||||
|
SpringApplicationBuilder builder = new SpringApplicationBuilder(NuclearApplication.class); |
||||
|
builder.bannerMode(Banner.Mode.CONSOLE); |
||||
|
builder.banner(new ResourceBanner(new ClassPathResource("banner.txt"))); |
||||
|
builder.run(args); |
||||
} |
} |
||||
|
|
||||
} |
} |
@ -0,0 +1,68 @@ |
|||||
|
package com.iflytop.nuclear.config; |
||||
|
|
||||
|
import com.iflytop.nuclear.exception.JWTAccessDeniedHandler; |
||||
|
import com.iflytop.nuclear.exception.JWTAuthenticationEntryPoint; |
||||
|
import com.iflytop.nuclear.filter.JWTAuthenticationFilter; |
||||
|
import com.iflytop.nuclear.filter.JWTAuthorizationFilter; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.beans.factory.annotation.Qualifier; |
||||
|
import org.springframework.context.annotation.Bean; |
||||
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; |
||||
|
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; |
||||
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity; |
||||
|
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; |
||||
|
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
||||
|
import org.springframework.security.config.http.SessionCreationPolicy; |
||||
|
import org.springframework.security.core.userdetails.UserDetailsService; |
||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
|
import org.springframework.web.cors.CorsConfiguration; |
||||
|
import org.springframework.web.cors.CorsConfigurationSource; |
||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc spring-security 配置类 |
||||
|
*/ |
||||
|
@EnableWebSecurity |
||||
|
@EnableGlobalMethodSecurity(prePostEnabled = true) |
||||
|
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
||||
|
|
||||
|
@Autowired |
||||
|
@Qualifier("userDetailsServiceImpl") |
||||
|
private UserDetailsService userDetailsService; |
||||
|
|
||||
|
@Bean |
||||
|
public BCryptPasswordEncoder bCryptPasswordEncoder() { |
||||
|
return new BCryptPasswordEncoder(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void configure(AuthenticationManagerBuilder auth) throws Exception { |
||||
|
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void configure(HttpSecurity http) throws Exception { |
||||
|
http.cors().and().csrf().disable() |
||||
|
.authorizeRequests() |
||||
|
// 注册接口,需要ADMIN用户才能访问 |
||||
|
.antMatchers("/account/register").hasRole("ADMIN") |
||||
|
// 其他都放行了 |
||||
|
.anyRequest().permitAll() |
||||
|
.and() |
||||
|
.addFilter(new JWTAuthenticationFilter(authenticationManager())) |
||||
|
.addFilter(new JWTAuthorizationFilter(authenticationManager())) |
||||
|
// 不需要session |
||||
|
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) |
||||
|
.and() |
||||
|
.exceptionHandling().authenticationEntryPoint(new JWTAuthenticationEntryPoint()) |
||||
|
.accessDeniedHandler(new JWTAccessDeniedHandler()); //添加无权限时的处理 |
||||
|
} |
||||
|
|
||||
|
@Bean |
||||
|
CorsConfigurationSource corsConfigurationSource() { |
||||
|
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); |
||||
|
source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); |
||||
|
return source; |
||||
|
} |
||||
|
} |
@ -0,0 +1,56 @@ |
|||||
|
package com.iflytop.nuclear.controller; |
||||
|
|
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import com.iflytop.nuclear.service.AccountService; |
||||
|
import com.iflytop.nuclear.utils.ResponseData; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.*; |
||||
|
|
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 用户接口 |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
@RestController |
||||
|
@RequestMapping("/account") |
||||
|
public class AccountController { |
||||
|
|
||||
|
@Autowired |
||||
|
AccountService accountService; |
||||
|
|
||||
|
/** |
||||
|
* 查询用户列表 |
||||
|
* @return |
||||
|
*/ |
||||
|
@GetMapping("/list") |
||||
|
public ResponseData getAccountList() { |
||||
|
log.info("-----------------查询账户列表开始-----------------"); |
||||
|
// 筛除password |
||||
|
// TODO |
||||
|
List<Account> accounts = accountService.list(); |
||||
|
log.info("-----------------查询账户列表结束-----------------"); |
||||
|
return ResponseData.success(accounts); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 注册接口,需要有ADMIN权限 |
||||
|
* @param registerUser |
||||
|
* @return |
||||
|
*/ |
||||
|
@PostMapping("/register") |
||||
|
@PreAuthorize("hasRole('ADMIN')") |
||||
|
public ResponseData registerAccount(@RequestBody Map<String,String> registerUser) { |
||||
|
log.info("-----------------注册账户开始-----------------"); |
||||
|
boolean register = accountService.register(registerUser.get("username"), registerUser.get("password")); |
||||
|
if (register) { |
||||
|
log.info("-----------------注册账户成功-----------------"); |
||||
|
return ResponseData.success(); |
||||
|
} |
||||
|
return ResponseData.fail("注册失败"); |
||||
|
} |
||||
|
} |
@ -0,0 +1,80 @@ |
|||||
|
package com.iflytop.nuclear.entity; |
||||
|
|
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import org.springframework.security.core.GrantedAuthority; |
||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||
|
|
||||
|
import java.util.Collection; |
||||
|
import java.util.Collections; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 自定义实现UserDetails |
||||
|
*/ |
||||
|
public class JwtUser implements UserDetails { |
||||
|
|
||||
|
private Long id; |
||||
|
private String username; |
||||
|
private String password; |
||||
|
private Collection<? extends GrantedAuthority> authorities; |
||||
|
|
||||
|
public JwtUser() { |
||||
|
} |
||||
|
|
||||
|
// 写一个能直接使用user创建jwtUser的构造器 |
||||
|
public JwtUser(Account user) { |
||||
|
id = user.getId(); |
||||
|
username = user.getUsername(); |
||||
|
password = user.getPassword(); |
||||
|
authorities = Collections.singleton(new SimpleGrantedAuthority(user.getRole())); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Collection<? extends GrantedAuthority> getAuthorities() { |
||||
|
return authorities; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getPassword() { |
||||
|
return password; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getUsername() { |
||||
|
return username; |
||||
|
} |
||||
|
|
||||
|
// 账号是否未过期 |
||||
|
@Override |
||||
|
public boolean isAccountNonExpired() { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 账号是否未锁定 |
||||
|
@Override |
||||
|
public boolean isAccountNonLocked() { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
// 账号凭证是否未过期 |
||||
|
@Override |
||||
|
public boolean isCredentialsNonExpired() { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean isEnabled() { |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
return "JwtUser{" + |
||||
|
"id=" + id + |
||||
|
", username='" + username + '\'' + |
||||
|
", password='" + password + '\'' + |
||||
|
", authorities=" + authorities + |
||||
|
'}'; |
||||
|
} |
||||
|
} |
@ -0,0 +1,25 @@ |
|||||
|
package com.iflytop.nuclear.exception; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import org.springframework.security.access.AccessDeniedException; |
||||
|
import org.springframework.security.web.access.AccessDeniedHandler; |
||||
|
|
||||
|
import javax.servlet.ServletException; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.IOException; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @description: 没有访问权限 |
||||
|
*/ |
||||
|
public class JWTAccessDeniedHandler implements AccessDeniedHandler { |
||||
|
@Override |
||||
|
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException { |
||||
|
httpServletResponse.setCharacterEncoding("UTF-8"); |
||||
|
httpServletResponse.setContentType("application/json; charset=utf-8"); |
||||
|
httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN); |
||||
|
String reason = "统一处理,原因:" + e.getMessage(); |
||||
|
httpServletResponse.getWriter().write(new ObjectMapper().writeValueAsString(reason)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.iflytop.nuclear.exception; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import org.springframework.security.core.AuthenticationException; |
||||
|
import org.springframework.security.web.AuthenticationEntryPoint; |
||||
|
|
||||
|
import javax.servlet.ServletException; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.IOException; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @description: 没有携带token或者token无效 |
||||
|
*/ |
||||
|
public class JWTAuthenticationEntryPoint implements AuthenticationEntryPoint { |
||||
|
@Override |
||||
|
public void commence(HttpServletRequest request, |
||||
|
HttpServletResponse response, |
||||
|
AuthenticationException authException) throws IOException, ServletException { |
||||
|
|
||||
|
response.setCharacterEncoding("UTF-8"); |
||||
|
response.setContentType("application/json; charset=utf-8"); |
||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN); |
||||
|
String reason = "统一处理,原因:" + authException.getMessage(); |
||||
|
response.getWriter().write(new ObjectMapper().writeValueAsString(reason)); |
||||
|
} |
||||
|
} |
@ -0,0 +1,27 @@ |
|||||
|
package com.iflytop.nuclear.exception; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @description: 自定义异常 |
||||
|
*/ |
||||
|
public class TokenIsExpiredException extends Exception { |
||||
|
|
||||
|
public TokenIsExpiredException() { |
||||
|
} |
||||
|
|
||||
|
public TokenIsExpiredException(String message) { |
||||
|
super(message); |
||||
|
} |
||||
|
|
||||
|
public TokenIsExpiredException(String message, Throwable cause) { |
||||
|
super(message, cause); |
||||
|
} |
||||
|
|
||||
|
public TokenIsExpiredException(Throwable cause) { |
||||
|
super(cause); |
||||
|
} |
||||
|
|
||||
|
public TokenIsExpiredException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { |
||||
|
super(message, cause, enableSuppression, writableStackTrace); |
||||
|
} |
||||
|
} |
@ -0,0 +1,77 @@ |
|||||
|
package com.iflytop.nuclear.filter; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.iflytop.nuclear.entity.JwtUser; |
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import com.iflytop.nuclear.utils.JwtTokenUtils; |
||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
|
import org.springframework.security.core.Authentication; |
||||
|
import org.springframework.security.core.AuthenticationException; |
||||
|
import org.springframework.security.core.GrantedAuthority; |
||||
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
||||
|
|
||||
|
import javax.servlet.FilterChain; |
||||
|
import javax.servlet.ServletException; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.IOException; |
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collection; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 该过滤器用于获取用户登录的信息 |
||||
|
*/ |
||||
|
public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { |
||||
|
|
||||
|
private AuthenticationManager authenticationManager; |
||||
|
|
||||
|
public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { |
||||
|
this.authenticationManager = authenticationManager; |
||||
|
super.setFilterProcessesUrl("/account/login"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Authentication attemptAuthentication(HttpServletRequest request, |
||||
|
HttpServletResponse response) throws AuthenticationException { |
||||
|
|
||||
|
// 从输入流中获取到登录的信息 |
||||
|
try { |
||||
|
Account loginUser = new ObjectMapper().readValue(request.getInputStream(), Account.class); |
||||
|
return authenticationManager.authenticate( |
||||
|
new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword(), new ArrayList<>()) |
||||
|
); |
||||
|
} catch (IOException e) { |
||||
|
e.printStackTrace(); |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 成功验证后调用的方法 |
||||
|
// 如果验证成功,就生成token并返回 |
||||
|
@Override |
||||
|
protected void successfulAuthentication(HttpServletRequest request, |
||||
|
HttpServletResponse response, |
||||
|
FilterChain chain, |
||||
|
Authentication authResult) throws IOException, ServletException { |
||||
|
|
||||
|
JwtUser jwtUser = (JwtUser) authResult.getPrincipal(); |
||||
|
System.out.println("jwtUser:" + jwtUser.toString()); |
||||
|
String role = ""; |
||||
|
Collection<? extends GrantedAuthority> authorities = jwtUser.getAuthorities(); |
||||
|
for (GrantedAuthority authority : authorities){ |
||||
|
role = authority.getAuthority(); |
||||
|
} |
||||
|
String token = JwtTokenUtils.createToken(jwtUser.getUsername(), role); |
||||
|
// 返回创建成功的token |
||||
|
// 但是这里创建的token只是单纯的token |
||||
|
// 按照jwt的规定,最后请求的时候应该是 `Bearer token` |
||||
|
response.setHeader("token", JwtTokenUtils.TOKEN_PREFIX + token); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException { |
||||
|
response.getWriter().write("authentication failed, reason: " + failed.getMessage()); |
||||
|
} |
||||
|
} |
@ -0,0 +1,73 @@ |
|||||
|
package com.iflytop.nuclear.filter; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
|
import com.iflytop.nuclear.exception.TokenIsExpiredException; |
||||
|
import com.iflytop.nuclear.utils.JwtTokenUtils; |
||||
|
import org.springframework.security.authentication.AuthenticationManager; |
||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority; |
||||
|
import org.springframework.security.core.context.SecurityContextHolder; |
||||
|
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; |
||||
|
|
||||
|
import javax.servlet.FilterChain; |
||||
|
import javax.servlet.ServletException; |
||||
|
import javax.servlet.http.HttpServletRequest; |
||||
|
import javax.servlet.http.HttpServletResponse; |
||||
|
import java.io.IOException; |
||||
|
import java.util.Collections; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 鉴权 |
||||
|
*/ |
||||
|
public class JWTAuthorizationFilter extends BasicAuthenticationFilter { |
||||
|
|
||||
|
public JWTAuthorizationFilter(AuthenticationManager authenticationManager) { |
||||
|
super(authenticationManager); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void doFilterInternal(HttpServletRequest request, |
||||
|
HttpServletResponse response, |
||||
|
FilterChain chain) throws IOException, ServletException { |
||||
|
|
||||
|
String tokenHeader = request.getHeader(JwtTokenUtils.TOKEN_HEADER); |
||||
|
// 如果请求头中没有Authorization信息则直接放行了 |
||||
|
if (tokenHeader == null || !tokenHeader.startsWith(JwtTokenUtils.TOKEN_PREFIX)) { |
||||
|
chain.doFilter(request, response); |
||||
|
return; |
||||
|
} |
||||
|
// 如果请求头中有token,则进行解析,并且设置认证信息 |
||||
|
try { |
||||
|
SecurityContextHolder.getContext().setAuthentication(getAuthentication(tokenHeader)); |
||||
|
} catch (TokenIsExpiredException e) { |
||||
|
//返回json形式的错误信息 |
||||
|
response.setCharacterEncoding("UTF-8"); |
||||
|
response.setContentType("application/json; charset=utf-8"); |
||||
|
response.setStatus(HttpServletResponse.SC_FORBIDDEN); |
||||
|
String reason = "统一处理,原因:" + e.getMessage(); |
||||
|
response.getWriter().write(new ObjectMapper().writeValueAsString(reason)); |
||||
|
response.getWriter().flush(); |
||||
|
return; |
||||
|
} |
||||
|
super.doFilterInternal(request, response, chain); |
||||
|
} |
||||
|
|
||||
|
// 这里从token中获取用户信息并新建一个token |
||||
|
private UsernamePasswordAuthenticationToken getAuthentication(String tokenHeader) throws TokenIsExpiredException { |
||||
|
String token = tokenHeader.replace(JwtTokenUtils.TOKEN_PREFIX, ""); |
||||
|
boolean expiration = JwtTokenUtils.isExpiration(token); |
||||
|
if (expiration) { |
||||
|
throw new TokenIsExpiredException("token超时了"); |
||||
|
} else { |
||||
|
String username = JwtTokenUtils.getUsername(token); |
||||
|
String role = JwtTokenUtils.getUserRole(token); |
||||
|
if (username != null) { |
||||
|
return new UsernamePasswordAuthenticationToken(username, null, |
||||
|
Collections.singleton(new SimpleGrantedAuthority(role)) |
||||
|
); |
||||
|
} |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
package com.iflytop.nuclear.mapper; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import org.apache.ibatis.annotations.Mapper; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
*/ |
||||
|
@Mapper |
||||
|
public interface AccountMapper extends BaseMapper<Account> { |
||||
|
|
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
package com.iflytop.nuclear.model; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.annotation.TableId; |
||||
|
import com.baomidou.mybatisplus.annotation.TableName; |
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.Builder; |
||||
|
import lombok.Data; |
||||
|
import lombok.NoArgsConstructor; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 账户model |
||||
|
*/ |
||||
|
@Data |
||||
|
@TableName("account") |
||||
|
@Builder |
||||
|
@NoArgsConstructor |
||||
|
@AllArgsConstructor |
||||
|
public class Account { |
||||
|
@TableId |
||||
|
private Long id; |
||||
|
private String username; |
||||
|
private String password; |
||||
|
private String role; |
||||
|
private String nickname; |
||||
|
} |
@ -0,0 +1,16 @@ |
|||||
|
package com.iflytop.nuclear.service; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
*/ |
||||
|
@Transactional |
||||
|
public interface AccountService extends IService<Account> { |
||||
|
|
||||
|
boolean register(String username, String password); |
||||
|
|
||||
|
Account findByUsername(String username); |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
package com.iflytop.nuclear.service; |
||||
|
|
||||
|
import com.iflytop.nuclear.entity.JwtUser; |
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Lazy; |
||||
|
import org.springframework.security.core.userdetails.UserDetails; |
||||
|
import org.springframework.security.core.userdetails.UserDetailsService; |
||||
|
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 使用springSecurity需要实现UserDetailsService接口供权限框架调用 |
||||
|
*/ |
||||
|
@Service |
||||
|
public class UserDetailsServiceImpl implements UserDetailsService { |
||||
|
|
||||
|
@Autowired |
||||
|
@Lazy |
||||
|
AccountService accountService; |
||||
|
|
||||
|
@Override |
||||
|
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
||||
|
Account user = accountService.findByUsername(username); |
||||
|
return new JwtUser(user); |
||||
|
} |
||||
|
} |
@ -0,0 +1,51 @@ |
|||||
|
package com.iflytop.nuclear.service.impl; |
||||
|
|
||||
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
|
import com.iflytop.nuclear.mapper.AccountMapper; |
||||
|
import com.iflytop.nuclear.model.Account; |
||||
|
import com.iflytop.nuclear.service.AccountService; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Lazy; |
||||
|
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
*/ |
||||
|
@Service |
||||
|
public class AccountServiceImpl extends ServiceImpl<AccountMapper, Account> implements AccountService { |
||||
|
|
||||
|
@Autowired |
||||
|
@Lazy |
||||
|
private BCryptPasswordEncoder bCryptPasswordEncoder; |
||||
|
|
||||
|
/** |
||||
|
* 注册用户 |
||||
|
* @param username |
||||
|
* @param password |
||||
|
* @return |
||||
|
*/ |
||||
|
@Override |
||||
|
public boolean register(String username, String password) { |
||||
|
Account account = Account.builder() |
||||
|
.username(username) |
||||
|
.password(bCryptPasswordEncoder.encode(password)) |
||||
|
.role("ROLE_USER") |
||||
|
.build(); |
||||
|
return this.save(account); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 根据用户名查找用户 |
||||
|
* @param username |
||||
|
* @return |
||||
|
*/ |
||||
|
@Override |
||||
|
public Account findByUsername(String username) { |
||||
|
QueryWrapper<Account> accountQueryWrapper = new QueryWrapper<>(); |
||||
|
accountQueryWrapper.eq("username", username); |
||||
|
Account account = this.getOne(accountQueryWrapper); |
||||
|
return account; |
||||
|
} |
||||
|
} |
@ -0,0 +1,68 @@ |
|||||
|
package com.iflytop.nuclear.utils; |
||||
|
|
||||
|
import io.jsonwebtoken.Claims; |
||||
|
import io.jsonwebtoken.ExpiredJwtException; |
||||
|
import io.jsonwebtoken.Jwts; |
||||
|
import io.jsonwebtoken.SignatureAlgorithm; |
||||
|
|
||||
|
import java.util.Date; |
||||
|
import java.util.HashMap; |
||||
|
|
||||
|
/** |
||||
|
* @author cool |
||||
|
* @desc 操作jwt token 的工具类 |
||||
|
*/ |
||||
|
public class JwtTokenUtils { |
||||
|
|
||||
|
public static final String TOKEN_HEADER = "Authorization"; |
||||
|
public static final String TOKEN_PREFIX = "Bearer "; |
||||
|
|
||||
|
private static final String SECRET = "eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE2NDg5ODg1MjAsInN1YiI6ImFkbWluIiwiY3JlYXR"; |
||||
|
private static final String ISS = "iflytop"; |
||||
|
|
||||
|
// 角色的key |
||||
|
private static final String ROLE_CLAIMS = "rol"; |
||||
|
|
||||
|
// 过期时间是3600秒,既是1个小时 |
||||
|
private static final long EXPIRATION = 3600L; |
||||
|
|
||||
|
// 创建token |
||||
|
public static String createToken(String username, String role) { |
||||
|
HashMap<String, Object> map = new HashMap<>(); |
||||
|
map.put(ROLE_CLAIMS, role); |
||||
|
return Jwts.builder() |
||||
|
.signWith(SignatureAlgorithm.HS512, SECRET) |
||||
|
.setClaims(map) |
||||
|
.setIssuer(ISS) |
||||
|
.setSubject(username) |
||||
|
.setIssuedAt(new Date()) |
||||
|
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION * 1000)) |
||||
|
.compact(); |
||||
|
} |
||||
|
|
||||
|
// 从token中获取用户名 |
||||
|
public static String getUsername(String token){ |
||||
|
return getTokenBody(token).getSubject(); |
||||
|
} |
||||
|
|
||||
|
// 获取用户角色 |
||||
|
public static String getUserRole(String token){ |
||||
|
return (String) getTokenBody(token).get(ROLE_CLAIMS); |
||||
|
} |
||||
|
|
||||
|
// 是否已过期 |
||||
|
public static boolean isExpiration(String token) { |
||||
|
try { |
||||
|
return getTokenBody(token).getExpiration().before(new Date()); |
||||
|
} catch (ExpiredJwtException e) { |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static Claims getTokenBody(String token){ |
||||
|
return Jwts.parser() |
||||
|
.setSigningKey(SECRET) |
||||
|
.parseClaimsJws(token) |
||||
|
.getBody(); |
||||
|
} |
||||
|
} |
@ -1,7 +1,7 @@ |
|||||
|
|
||||
spring: |
spring: |
||||
datasource: |
datasource: |
||||
url: jdbc:mysql://127.0.0.1:3306/boditech?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai |
|
||||
|
url: jdbc:mysql://127.0.0.1:3306/nuclear?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai |
||||
username: root |
username: root |
||||
password: root |
password: root |
||||
driverClassName: com.mysql.cj.jdbc.Driver |
driverClassName: com.mysql.cj.jdbc.Driver |
Write
Preview
Loading…
Cancel
Save
Reference in new issue