springboot+springsecurity+jwt 完整登录验证流程 小白都会!

导入springsecurity的依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

在这里插入图片描述打
就会有安全管理验证密码
直接上localhost
账号:user 密码:刚刚生成的:密码字段
登录成功说明导入依赖成功!
二:去实体类实现UserDetails接口
在这里插入图片描述
并实现它的方法!
在这里插入图片描述
第三步编写controller以及service login登录实现方法

    @Override
    public Result login(LoginVo loginVo) {
        log.info("1.开始登录");
        UserDetails userDetails = userDetailsService.loadUserByUsername(loginVo.getUsername());
        log.info("2.判断账号密码是正确");
        if(userDetails == null || !passwordEncoder.matches(loginVo.getPassword(),userDetails.getPassword())){
              return Result.fail("账号或密码错误,请重新输入!");
        }
        if(!userDetails.isEnabled()){
            return Result.fail("该账号已经被禁用,请联系管理员!");
        }
        log.info("3.将该数据存入security容器中");
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null);
        SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
        log.info("4.根据登录信息获取token");
        String token="";
        HashMap<String, String> map = new HashMap<>();
        map.put("tokenHead",tokenHead);
        map.put("token",token);
        return Result.success("登录成功!",map);
    }

这里的token需要后面jwt生成的token
以及yml配置jwt相应的信息

jwt:
  #请求头
  tokenHeader: Authorization
  #签名加密
  secret: 123456
  #过期时间
  expiration: 1800
  #token头部
  tokenHead: 'Bearer '

编写utils.jwt工具类

package com.intelligent.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * jwt生成token
 */
@Component
public class JwtTokenUtil {

    private static final String CLAIM_KEY_USERNAME = "username";
    private static final String CLAIM_KEY_CREATED = "created";

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long expiration;

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateJwt(claims);
    }

    /**
     * 根据荷载生成token
     * @param claims
     * @return
     */
    public String generateJwt(Map<String, Object> claims) {
        return Jwts.builder()
                .setClaims(claims)
                .signWith(SignatureAlgorithm.HS512, secret)
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
                .compact();
    }

    /**
     * 从token中获取用户名
     * @param token
     * @return
     */
    public String getUserNameFromToken(String token) {
        Claims tokenBody = getTokenBody(token);
        String username = (String) tokenBody.get(CLAIM_KEY_USERNAME);
        return  username;
    }

    /**
     * token是否过期
     * @param token
     * @return
     */
    public boolean isExpiration(String token) {
        return getTokenBody(token).getExpiration().before(new Date());
    }

    /**
     * 验证token是否有效
     * @param token
     * @param userDetails
     * @return
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        String username = getUserNameFromToken(token);
        return username.equals(userDetails.getUsername()) && !isExpiration(token);
    }

    /**
     * 判断token是否可以被刷新
     * @param token
     * @return
     */
    public boolean canRefresh(String token) {
        return !isExpiration(token);
    }

    /**
     * 刷新token
     * @param token
     * @return
     */
    public String refreshToken(String token) {
        Claims claims = getTokenBody(token);
        claims.setExpiration(new Date());
        return generateJwt(claims);
    }

    /**
     * 获取token中的信息
     * @param token
     * @return
     */
    public Claims getTokenBody(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            return null;
        }

    }

}

开始编写 Security相关的配置文件

package com.intelligent.security;

import com.intelligent.entity.SysUser;
import com.intelligent.handler.JwtAccessDeniedHandler;
import com.intelligent.handler.JwtAuthenticationEntryPoint;
import com.intelligent.handler.JwtAuthenticationFilter;
import com.intelligent.mapper.SysUserMapper;
import com.intelligent.security.content.SecurityContents;
import com.intelligent.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.builders.WebSecurity;
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.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;


//权限的基本配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig  extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Autowired
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

//
    @Override
    public void configure(WebSecurity web)  {

        web.ignoring().mvcMatchers(SecurityContents.WHITE_LIST)
                .antMatchers(SecurityContents.WHITE_LIST);
//        web.ignoring()
//                .antMatchers("/doc.html",
//                                        "/webjars/**",
//                                        "/img.icons/**",
//                                        "/swagger-resources/**",
//                                        "/v2/api-docs");



    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        // 1.使用jwt 关闭跨域攻击
        http.csrf().disable();
        //关闭session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        //3.请求需要认证才能访问
        http.authorizeRequests().anyRequest().authenticated();
        //4.关闭缓存
        http.headers().cacheControl();
        //5.token过滤器,校验token
        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        //6.没有登录,没有权限访问资源自定义
        http.exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).accessDeniedHandler(jwtAccessDeniedHandler);


    }

    //将自定义的登录 将它放入security中进行认证
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }


    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }


}

以及需要放行的SecurityContents文件内容

package com.intelligent.security.content;

public class SecurityContents {

    public static  final String[] WHITE_LIST = {
            //登录接口
            "/user/login",
            "/user/captcha",
            "/user/captcha1",
            "/user/test",
            "/api/user/login",

            //swagger
            "/doc.html",
            "/favicon.ico",
            "/swagger-ui.html",
            "/webjars/**",
            "/swagger-resources/**",
            "/v2/*",
            "/configuration/ui",
            "/configuration/security",

    };

}

再重新实现UserDetails loadUserByUsername的方法

package com.intelligent.security.service;

import com.intelligent.entity.SysRole;
import com.intelligent.entity.SysUser;
import com.intelligent.mapper.SysUserMapper;
import lombok.extern.slf4j.Slf4j;
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;

import java.util.ArrayList;

@Service
@Slf4j
public class UserDetailServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser user = userMapper.findByUsername(username);
        if(user==null){
            throw new UsernameNotFoundException("账号或者密码错误!");
        }
        if(user.getAdmin()){
            ArrayList<SysRole> list = new ArrayList<>();
            SysRole role = new SysRole();
            role.setCode("admin");
            list.add(role);
            user.setRoles(list);
            //user.setMenus();
        }else {
            user.setRoles(userMapper.findRoles(user.getId()));
            user.setPermissions(userMapper.findPermissionById(user.getId()));

        }

        return user;
    }
}

因为Security需要过滤前端传入的接口数据所以要配置三个配置类结构
在这里插入图片描述
jwt的三个配置类分别过滤权限,token,以及过期时间,登录功能

package com.intelligent.handler;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 查看是否有对应的权限和功能
 */
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setStatus(403);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(Result.fail("权限不足,请联系管理员")));
        writer.flush();
        writer.close();
    }
}

package com.intelligent.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


//当用户未登录和token过期 时访问资源
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(401);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(Result.fail("您尚未登录,请登录")));
        writer.flush();
        writer.close();
    }
}

package com.intelligent.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.intelligent.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;


//当用户未登录和token过期 时访问资源
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        response.setStatus(401);
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json");
        PrintWriter writer = response.getWriter();
        writer.write(new ObjectMapper().writeValueAsString(Result.fail("您尚未登录,请登录")));
        writer.flush();
        writer.close();
    }
}

三个类分别过滤以及再上述的SecurityConfig中配置
上述配置好后记得修改SysUserServiceImpl 里面token ,将“” 改成用token工具类生产的工具
在这里插入图片述
前端访问获取到token信息 获取成功!以后每个资源访问都必须带上这个头部信息

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值