SpringBoot添加密码安全配置以及Jwt配置

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后面要加一个空格。

 然后即可正常使用。

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot 配置 JWT 的步骤如下: 1. 添加依赖 在 pom.xml 文件中添加以下依赖: ```xml <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 2. 配置 JWT 工具类 创建一个 JWT 工具类,用于生成和验证 JWT。 ```java @Component public class JwtUtil { private String secret = "mysecretkey"; public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, userDetails.getUsername()); } private String createToken(Map<String, Object> claims, String subject) { long now = System.currentTimeMillis(); return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(now)) .setExpiration(new Date(now + 1000 * 60 * 60 * 24)) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public Boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } private Date extractExpiration(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration(); } private String extractUsername(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject(); } } ``` 在上述代码中,我们使用了 jjwt 库来生成和验证 JWT。在生成 JWT 时,我们设置了 JWT 的有效期为一天。 3. 配置 Spring Security 我们需要在 Spring Security 中配置 JWT,以便使用 JWT 来进行身份验证。 ```java @Configuration @EnableWebSecurity public class SecurityConfigurer extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService); } @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests().antMatchers("/authenticate").permitAll(). anyRequest().authenticated().and(). exceptionHandling().and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/security", "/swagger-ui.html", "/webjars/**"); } } ``` 在上述代码中,我们配置了一个自定义的 UserDetailsService,并将它注入到 AuthenticationManagerBuilder 中。我们还在 HttpSecurity 中添加了一个自定义的 JwtRequestFilter,用于验证 JWT。 4. 创建控制器 最后,我们创建一个控制器,用于生成 JWT。 ```java @RestController public class JwtAuthenticationController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JwtUtil jwtUtil; @Autowired private MyUserDetailsService userDetailsService; @RequestMapping(value = "/authenticate", method = RequestMethod.POST) public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword()) ); } catch (BadCredentialsException e) { throw new Exception("Incorrect username or password", e); } final UserDetails userDetails = userDetailsService .loadUserByUsername(authenticationRequest.getUsername()); final String token = jwtUtil.generateToken(userDetails); return ResponseEntity.ok(new AuthenticationResponse(token)); } } ``` 在上述代码中,我们使用了 AuthenticationManager 来验证用户名和密码,并使用 UserDetails 对象生成 JWT。最后,我们返回 JWT 给客户端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值