Maven仓库(依赖查找)
1、SpringBoot安全访问配置
首先添加依赖 spring-boot-starter-security
然后之后每次启动项目之后,访问任何的请求都会要求输入密码才能请求。(如下)
在没有配置的情况下,默认用户名为 user ,密码在控制台中查找(如下)。
- 修改SpringBoot安全访问配置,首先要创建一个UserDetailsServiceImpl类来继承UserDetailsService来接入数据库信息,然后再实现一个utils的工具类UserDetailsImpl来实现UserDetails。最后的逻辑就是在那个安全验证中输入你的账号和密码,如果在数据库中找到了username就将这个条字段作为通关令牌存起来,当退出的时候再删除
- 注:这里如果没有加密的话要使用的话在密码那个字段的属性中加一个{noop}
添加安全访问的配置文件如下。
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
public UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
User user = userMapper.selectOne(queryWrapper);
if(user == null)
return (UserDetails) new RuntimeException("用户不存在!");
return new UserDetailsImpl(user);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserDetailsImpl implements UserDetails {
private User user;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return user.getPasswd();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
以上配置添加之后即可正常使用数据库中的密码进行验证。前提是userMapper可以正常使用,进行查找用户。
如果数据库中的密码没有加密,可以通过测试类将密码进行加密,然后用加密之后的字符串与数据库中的数据进行替换即可。(如下)
@SpringBootTest
class TestJwtApplicationTests {
@Test
void contextLoads() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String encode = bCryptPasswordEncoder.encode("123456");
System.out.println(encode);
}
}
2、 配置JWT验证
2.1、配置类的编写添加
@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);
}
User user = userMapper.selectById(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);
}
}
下面类里面的JWT_KEY的字符串是必须自己随机生成然后长度是有限制的如果后期报错记得修改长度。这个东西很重要,是你密码的加密规则。
@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();
}
}
然后修改之前的SecurityConfig配置类,里面的configure方法里面的.antMatchers里面追加的url是不需要验证就能进行使用的请求,比如登录注册等url请求的放开,需要的话可以一直追加。
@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/login", "/user/account/register/").permitAll()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
}
}
2.2、jwt的登录验证并且获取验证码
编写LoginServiceImpl,可以进行登录的验证,以及获取token并且返回给用户。之后访问其他请求的时候都需要验证该token验证身份,登录失败会报异常。
@Service
public class LoginServiceImpl{
@Autowired
// 认证管理器
private AuthenticationManager authenticationManager;
public Map<String, String> getToken(String username, String password) {
// 将输入进来的username和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;
}
}
前端正常调用接口即可,将获取的token进行存储即可,用于,存储位置尽量安全。
2.3、未放开url请求的处理
通过以下操作可以直接从token中获取用户的信息,然后通过用户的信息进行验证并返回用户需要的信息。
@Service
public class InfoServiceImpl {
@Autowired
private UserMapper userMapper;
public Map getInfoById() {
Map data = new HashMap<>();
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl loginUser = (UserDetailsImpl)token.getPrincipal();
User user = loginUser.getUser();
user.setPasswd("");
data.put("user", user);
data.put("error_message", "success");
return data;
}
}
前端的请求中需要加上token,注意Bearer后面要加一个空格。
然后即可正常使用。