parent
148e0318f1
commit
2c8c6c2d87
15 changed files with 594 additions and 68 deletions
@ -0,0 +1,51 @@ |
||||
package cc.bnblogs.springdatajpa.config; |
||||
|
||||
import cc.bnblogs.springdatajpa.config.filter.JwtAuthenticationTokenFilter; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.http.HttpMethod; |
||||
import org.springframework.security.authentication.AuthenticationManager; |
||||
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.crypto.bcrypt.BCryptPasswordEncoder; |
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
||||
|
||||
/** |
||||
* @description: |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 17:06 |
||||
*/ |
||||
@Configuration |
||||
@EnableWebSecurity |
||||
public class SecurityConfig extends WebSecurityConfigurerAdapter { |
||||
@Autowired |
||||
private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; |
||||
|
||||
@Bean |
||||
public PasswordEncoder passwordEncoder() { |
||||
return new BCryptPasswordEncoder(); |
||||
} |
||||
|
||||
@Bean |
||||
@Override |
||||
public AuthenticationManager authenticationManagerBean() throws Exception { |
||||
return super.authenticationManagerBean(); |
||||
} |
||||
|
||||
@Override |
||||
protected void configure(HttpSecurity http) throws Exception { |
||||
http.csrf().disable() |
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) |
||||
.and() |
||||
.authorizeRequests() |
||||
.antMatchers("/user/account/token/", "/user/account/register/").permitAll() |
||||
.antMatchers(HttpMethod.OPTIONS).permitAll() |
||||
.anyRequest().authenticated(); |
||||
|
||||
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); |
||||
} |
||||
} |
@ -0,0 +1,61 @@ |
||||
package cc.bnblogs.springdatajpa.config.filter; |
||||
|
||||
import cc.bnblogs.springdatajpa.mapper.UserMapper; |
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import cc.bnblogs.springdatajpa.service.impl.security.UserDetailsImpl; |
||||
import cc.bnblogs.springdatajpa.utils.JwtUtil; |
||||
import com.sun.istack.internal.NotNull; |
||||
import io.jsonwebtoken.Claims; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
import org.springframework.security.core.context.SecurityContextHolder; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.util.StringUtils; |
||||
import org.springframework.web.filter.OncePerRequestFilter; |
||||
|
||||
import javax.servlet.FilterChain; |
||||
import javax.servlet.ServletException; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import java.io.IOException; |
||||
|
||||
@Component |
||||
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { |
||||
@Autowired |
||||
private UserMapper userMapper; |
||||
|
||||
@Override |
||||
protected void doFilterInternal(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull FilterChain filterChain) throws ServletException, IOException { |
||||
String token = request.getHeader("Authorization"); |
||||
|
||||
if (!StringUtils.hasText(token) || !token.startsWith("Bearer ")) { |
||||
filterChain.doFilter(request, response); |
||||
return; |
||||
} |
||||
|
||||
token = token.substring(7); |
||||
|
||||
String userid; |
||||
try { |
||||
Claims claims = JwtUtil.parseJWT(token); |
||||
userid = claims.getSubject(); |
||||
} catch (Exception e) { |
||||
throw new RuntimeException(e); |
||||
} |
||||
// 这里不同的ORM实现可能不同,这里用的是JPA
|
||||
// 就是根据用户id获取user
|
||||
User user = userMapper.findUserById(Integer.parseInt(userid)); |
||||
|
||||
if (user == null) { |
||||
throw new RuntimeException("用户名未登录"); |
||||
} |
||||
|
||||
UserDetailsImpl loginUser = new UserDetailsImpl(user); |
||||
UsernamePasswordAuthenticationToken authenticationToken = |
||||
new UsernamePasswordAuthenticationToken(loginUser, null, null); |
||||
|
||||
SecurityContextHolder.getContext().setAuthentication(authenticationToken); |
||||
|
||||
filterChain.doFilter(request, response); |
||||
} |
||||
} |
@ -0,0 +1,30 @@ |
||||
package cc.bnblogs.springdatajpa.service.impl.security; |
||||
|
||||
import cc.bnblogs.springdatajpa.mapper.UserMapper; |
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
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; |
||||
|
||||
/** |
||||
* @description: |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 17:42 |
||||
*/ |
||||
|
||||
@Service |
||||
public class UserDetailServiceImpl implements UserDetailsService { |
||||
|
||||
@Autowired |
||||
private UserMapper userMapper; |
||||
@Override |
||||
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { |
||||
User user = userMapper.getUserByName(username); |
||||
if (user==null) { |
||||
throw new RuntimeException("用户不存在"); |
||||
} |
||||
return new UserDetailsImpl(user); |
||||
} |
||||
} |
@ -0,0 +1,56 @@ |
||||
package cc.bnblogs.springdatajpa.service.impl.security; |
||||
|
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import lombok.AllArgsConstructor; |
||||
import lombok.Data; |
||||
import org.springframework.security.core.GrantedAuthority; |
||||
import org.springframework.security.core.userdetails.UserDetails; |
||||
|
||||
import java.util.Collection; |
||||
|
||||
/** |
||||
* @description: |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 17:51 |
||||
*/ |
||||
@Data |
||||
@AllArgsConstructor |
||||
public class UserDetailsImpl implements UserDetails { |
||||
|
||||
private User user; |
||||
@Override |
||||
public Collection<? extends GrantedAuthority> getAuthorities() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String getPassword() { |
||||
return user.getPassword(); |
||||
} |
||||
|
||||
@Override |
||||
public String getUsername() { |
||||
return user.getName(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAccountNonExpired() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAccountNonLocked() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isCredentialsNonExpired() { |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean isEnabled() { |
||||
return true; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,39 @@ |
||||
package cc.bnblogs.springdatajpa.service.impl.user; |
||||
|
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import cc.bnblogs.springdatajpa.service.impl.security.UserDetailsImpl; |
||||
import cc.bnblogs.springdatajpa.utils.JwtUtil; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.security.authentication.AuthenticationManager; |
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
import org.springframework.security.core.Authentication; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @description: 根据用户名和密码返回一个jwt_token |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 21:34 |
||||
*/ |
||||
@Service |
||||
public class LoginServiceImpl { |
||||
@Autowired |
||||
private AuthenticationManager authenticationManager; |
||||
|
||||
public Map<String, String> getToken(String username, String password) { |
||||
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password); |
||||
|
||||
Authentication authenticate = authenticationManager.authenticate(authenticationToken); |
||||
UserDetailsImpl loginUser = (UserDetailsImpl) authenticate.getPrincipal(); |
||||
User user = loginUser.getUser(); |
||||
String jwt = JwtUtil.createJWT(user.getId().toString()); |
||||
|
||||
Map<String, String> map = new HashMap<>(); |
||||
map.put("error_message", "success"); |
||||
map.put("token", jwt); |
||||
|
||||
return map; |
||||
} |
||||
} |
@ -0,0 +1,87 @@ |
||||
package cc.bnblogs.springdatajpa.service.impl.user; |
||||
|
||||
import cc.bnblogs.springdatajpa.mapper.UserMapper; |
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import org.springframework.beans.factory.annotation.Autowired; |
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @description: 用户注册 |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 22:12 |
||||
*/ |
||||
@Service |
||||
public class RegisterServiceImpl { |
||||
|
||||
@Autowired |
||||
private UserMapper userMapper; |
||||
|
||||
@Autowired |
||||
private PasswordEncoder passwordEncoder; |
||||
|
||||
public Map<String, String> register(String username, String password, String confirmedPassword) { |
||||
Map<String, String> map = new HashMap<>(); |
||||
if (username == null) { |
||||
map.put("error_message", "用户名不能为空"); |
||||
return map; |
||||
} |
||||
|
||||
if (password == null || confirmedPassword == null) { |
||||
map.put("error_message", "密码不能为空"); |
||||
return map; |
||||
} |
||||
//删除首尾的空白字符
|
||||
username = username.trim(); |
||||
if (username.length() == 0) { |
||||
map.put("error_message", "用户名不能为空"); |
||||
return map; |
||||
} |
||||
|
||||
if (password.length() == 0 || confirmedPassword.length() == 0) { |
||||
map.put("error_message", "密码不能为空"); |
||||
return map; |
||||
} |
||||
|
||||
if (username.length() > 100) { |
||||
map.put("error_message", "用户名长度不能大于100"); |
||||
return map; |
||||
} |
||||
|
||||
if (password.length() > 100 || confirmedPassword.length() > 100) { |
||||
map.put("error_message", "密码不能大于100"); |
||||
return map; |
||||
} |
||||
|
||||
if (!password.equals(confirmedPassword)) { |
||||
map.put("error_message", "两次输入的密码不一致"); |
||||
return map; |
||||
} |
||||
|
||||
//查询用户名是否重复
|
||||
|
||||
User user = userMapper.getUserByName(username); |
||||
|
||||
if (user != null) { |
||||
map.put("error_message", "用户名已存在"); |
||||
return map; |
||||
} |
||||
|
||||
// 添加一个新用户
|
||||
String encodedPassword = passwordEncoder.encode(password); |
||||
|
||||
User u = new User(); |
||||
u.setName(username); |
||||
u.setAge(18); |
||||
u.setEmail("xxxxx"); |
||||
u.setPassword(encodedPassword); |
||||
userMapper.save(u); |
||||
|
||||
map.put("error_message", "success"); |
||||
return map; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,36 @@ |
||||
package cc.bnblogs.springdatajpa.service.impl.user; |
||||
|
||||
import cc.bnblogs.springdatajpa.pojo.User; |
||||
import cc.bnblogs.springdatajpa.service.impl.security.UserDetailsImpl; |
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
||||
import org.springframework.security.core.context.SecurityContextHolder; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* @description: 用户信息 |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 22:12 |
||||
*/ |
||||
@Service |
||||
public class UserInfoServiceImpl { |
||||
|
||||
/** |
||||
* 根据token返回用户信息 |
||||
* @return map存储的信息 |
||||
*/ |
||||
public Map<String, String> getInfo() { |
||||
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); |
||||
|
||||
UserDetailsImpl loginUser = (UserDetailsImpl) authentication.getPrincipal(); |
||||
User user = loginUser.getUser(); |
||||
|
||||
Map<String, String> map = new HashMap<>(); |
||||
map.put("error_message", "success"); |
||||
map.put("id", user.getId().toString()); |
||||
map.put("name", user.getName()); |
||||
return map; |
||||
} |
||||
} |
@ -0,0 +1,68 @@ |
||||
package cc.bnblogs.springdatajpa.utils; |
||||
|
||||
/** |
||||
* @description: |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 20:33 |
||||
*/ |
||||
|
||||
import io.jsonwebtoken.Claims; |
||||
import io.jsonwebtoken.JwtBuilder; |
||||
import io.jsonwebtoken.Jwts; |
||||
import io.jsonwebtoken.SignatureAlgorithm; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
import javax.crypto.SecretKey; |
||||
import javax.crypto.spec.SecretKeySpec; |
||||
import java.util.Base64; |
||||
import java.util.Date; |
||||
import java.util.UUID; |
||||
|
||||
@Component |
||||
public class JwtUtil { |
||||
public static final long JWT_TTL = 60 * 60 * 1000L * 24 * 14; // 有效期14天
|
||||
public static final String JWT_KEY = "SDFGjhdsfalshdfHFdsjkdsfds121232131afasdfac"; |
||||
|
||||
public static String getUUID() { |
||||
return UUID.randomUUID().toString().replaceAll("-", ""); |
||||
} |
||||
|
||||
public static String createJWT(String subject) { |
||||
JwtBuilder builder = getJwtBuilder(subject, null, getUUID()); |
||||
return builder.compact(); |
||||
} |
||||
|
||||
private static JwtBuilder getJwtBuilder(String subject, Long ttlMillis, String uuid) { |
||||
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256; |
||||
SecretKey secretKey = generalKey(); |
||||
long nowMillis = System.currentTimeMillis(); |
||||
Date now = new Date(nowMillis); |
||||
if (ttlMillis == null) { |
||||
ttlMillis = JwtUtil.JWT_TTL; |
||||
} |
||||
|
||||
long expMillis = nowMillis + ttlMillis; |
||||
Date expDate = new Date(expMillis); |
||||
return Jwts.builder() |
||||
.setId(uuid) |
||||
.setSubject(subject) |
||||
.setIssuer("sg") |
||||
.setIssuedAt(now) |
||||
.signWith(signatureAlgorithm, secretKey) |
||||
.setExpiration(expDate); |
||||
} |
||||
|
||||
public static SecretKey generalKey() { |
||||
byte[] encodeKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY); |
||||
return new SecretKeySpec(encodeKey, 0, encodeKey.length, "HmacSHA256"); |
||||
} |
||||
|
||||
public static Claims parseJWT(String jwt) throws Exception { |
||||
SecretKey secretKey = generalKey(); |
||||
return Jwts.parserBuilder() |
||||
.setSigningKey(secretKey) |
||||
.build() |
||||
.parseClaimsJws(jwt) |
||||
.getBody(); |
||||
} |
||||
} |
@ -0,0 +1,35 @@ |
||||
package cc.bnblogs.springdatajpa.config; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
||||
import org.springframework.security.crypto.password.PasswordEncoder; |
||||
import sun.security.util.Password; |
||||
|
||||
import static org.junit.jupiter.api.Assertions.*; |
||||
|
||||
/** |
||||
* @description: |
||||
* @author: zfp@bnblogs.cc |
||||
* @date: 2023/3/8 17:13 |
||||
*/ |
||||
@SpringBootTest |
||||
class SecurityConfigTest { |
||||
|
||||
@Test |
||||
void passwordEncoder() { |
||||
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); |
||||
// 对明文加密
|
||||
System.out.println(passwordEncoder.encode("123456")); |
||||
System.out.println(passwordEncoder.encode("123456")); |
||||
System.out.println(passwordEncoder.encode("123456")); |
||||
System.out.println(passwordEncoder.encode("123456")); |
||||
System.out.println(passwordEncoder.encode("123456")); |
||||
|
||||
// 明文和密文匹配
|
||||
// true
|
||||
System.out.println(passwordEncoder.matches("123456","$2a$10$8ew9orusgSjo4fRBODHgBug6aOkJwaq3ikAOVRAtwPkCqVMVLJI.O")); |
||||
// false
|
||||
System.out.println(passwordEncoder.matches("1234567","$2a$10$8ew9orusgSjo4fRBODHgBug6aOkJwaq3ikAOVRAtwPkCqVMVLJI.O")); |
||||
} |
||||
} |
Loading…
Reference in new issue