java实现基于token认证

标签: token、java实现token认证、后台验证token

token验证是前后端交互中用的比较频繁的功能,这里我们的token采用前端cookie和后端session的方式来实现

首先token认证是用户通过浏览器登录成功后,后台返给前端的一个唯一标识,前端可以通过这个标识来访问后台接口

下面代码是我们从用户的登录到登录后的token认证的实现

没有异常处理的朋友可以参考一下【java自定义异常处理和抛出异常】传送门https://blog.csdn.net/qq_39997045/article/details/112068918

1. 用户实体类

@Data
public class UserInfoQuery {

    /**
     * 用户主键
     */
    private String id;

    /**
     * 用户姓名
     */
    private String userName;

    /**
     * 昵称(用户名)
     */
    private String nickname;

    /**
     * 密码
     */
    private String password;

    /**
     * 邮箱
     */
    private String mail;

    /**
     * 手机号
     */
    private String phone;

    /**
     * 所属部门
     */
    private String deptId;

    /**
     * 部门名称
     */
    private String deptName;

    /**
     * 登陆次数
     */
    private Integer logincount;

    /**
     * 权限
     */
    private String roles;

    /**
     * 用户状态
     */
    private Integer status;

    /**
     * accessToken
     */
    private String accessToken;
}

2. 前端调用登录验证码来建立与后台的连接

    这里我们将随机验证码返回给前端并将验证码保存到session中

   "token:login:varicode:" + loginName; 将当前登录的用户和登录验证码存入redis

/**
 * 获取登录图形验证码
 */
@ApiOperation("获取登录图形验证码")
@GetMapping("/getLoginCode")
public Map<String, Object> getLoginCode(
        @ApiParam(value = "账号",required = false) @RequestParam(value = "loginName",required = false)String loginName,
        HttpServletRequest request) throws ReturnException {
    Map<String, Object> map = new HashMap<String, Object>();

    // 验证码
    String randomStr = StringUtil.getRandomStr(true, 4);
    String key = "token:login:varicode:" + loginName;

    try {
        redisTemplate.opsForValue().set(key, randomStr);
    } catch (Exception e) {
        throw new ReturnException("0", "发送失败");
    }

    map.put("ret", "1");
    map.put("msg", "成功");
    map.put("data", randomStr);
    return map;
}

3. 用户登录

    controller层进行业务分发

    先验证用户用户名密码是否正确

    再将token等数据通过HttpServletResponse传给前端

    登录后记得将验证码删掉

/**
 * 用户登录
 */
@ApiOperation("用户登录")
@PostMapping("/webLogin")
public Map<String, Object> webLogin(
        @ApiParam(value = "用户名",required = false) @RequestParam(value = "loginName",required = false)String loginName,
        @ApiParam(value = "密码",required = false) @RequestParam(value = "password",required = false)String password,
        @ApiParam(value = "图形验证码",required = false) @RequestParam(value = "varicode",required = false)String varicode,
        HttpServletRequest request, HttpServletResponse resp) throws ReturnException {
    Map<String, Object> map = new HashMap<String, Object>();

    UserInfoQuery userInfo = userInfoService.webLogin(loginName, password, varicode);
    //设置tokenKey
    String tokenKey = StringUtil.getSUUID();
    if (userInfo!=null) {
        userInfo.setAccessToken(tokenKey);
        userInfoService.setSession(tokenKey, userInfo, resp);
    }

    // 登录后记得将验证码删掉
    redisTemplate.delete("token:login:varicode:" + loginName);
    map.put("data", userInfo);
    map.put("msg", "成功");
    map.put("ret", "1");
    return map;
}

    Impl实现类

        这里需要判断登录token是否有效

@Override
public UserInfoQuery webLogin(String loginName, String password, String varicode) throws ReturnException {

    String key = "token:login:varicode:" + loginName;

    if (!redisTemplate.hasKey(key)) {
        throw new ReturnException("3", "登录超时");
    }

    if (!varicode.equals(redisTemplate.opsForValue().get(key))) {
        throw new ReturnException("3", "验证码错误");
    }

    UserInfoQuery userInfoQuery = userInfoMapper.findLoginIsRepeat(loginName);

    if (userInfoQuery==null) {
        throw new ReturnException("3", "用户不存在");
    }

    if (!password.equals(userInfoQuery.getPassword())) {
        throw new ReturnException("3", "账号或密码错误");
    }

    return userInfoQuery;
}

dao实现

    @Select("select id, user_name, nickname, password, mail, phone, dept_id, dept_name, logincount, roles, status from app_user_info " +
            "where nickname = #{loginName} or phone = #{loginName} or mail = #{loginName}")
    UserInfoQuery findLoginIsRepeat(@Param("loginName") String loginName);

登陆成功后,设置浏览器token信息

    这里将用户的token和用户的对象存入session

    将token存入header头和cookie中并通过HttpServletResponse传给前端

    /**
     * 设置浏览器token
     */
    @Override
    public boolean setSession(String tokenKey, UserInfoQuery userInfoQuery, HttpServletResponse resp){
        // 将token存入缓存中
        // token在项目中使用得场景很多, 有的有效期较短,比如验证码、短信验证,也有有效期长的,比如登录后
        // 这里在token后加个visit就是声明我是一个访问的token,与其他token进行区分
        redisTemplate.opsForValue().set("token:visit:"+ tokenKey,tokenKey);
        // 将用户的信息存入redis中, 存用户数据(这里没用到,但实际开发会通过缓存获取对象数据)
        redisTemplate.opsForValue().set("token:data:visit:" + tokenKey, userInfoQuery);
        //设置cookie 这是一段伪代码,具体实现根据项目实际情况进行设置
        Cookie cookie = new Cookie("access_token", tokenKey);
        // 将cookie存入resp相应中
        resp.addCookie(cookie);
        // 将token翻入消息头
        resp.addHeader("access_token",tokenKey);
        userInfoQuery.setAccessToken(null);
        return true;
    }

下面附postman测试

发送验证码

登录

后端返给前端的token

到这里一个简单的登录就做好了, 下面我们来进行token的认证

这里我们只做一个简单的案例

添加 LoginInterceptor 类继承 HandlerInterceptor 实现拦截

这里只做token不为空和是否有效


@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Autowired
    private RedisTemplate redisTemplate;

    //  拼接token的键
    String tokenKey = "token:visit:";

    //  在请求处理之前调用,只有返回true才会执行要执行的请求
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {

        httpServletResponse.setCharacterEncoding("UTF-8");
        String token=httpServletRequest.getHeader("access_token");
        if (null==token){
           throw new ReturnException("3", "请求非法");
        }

        // 判断token是否存在
        String key = tokenKey+token;
        if(!redisTemplate.hasKey(key)) {
            throw new ReturnException("3", "token失效请重新登录");
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {

    }


}

2 通过自定义拦截器去拦截请求

    通过重写 addInterceptors 来实现请求拦截,这里拦截的是 LoginInterceptor

@Configuration
@EnableSwagger2
public class BeanConfig implements WebMvcConfigurer {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Autowired
    private PubElConfig elConfig;

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/itlir").setViewName("/index/index");
        registry.addViewController("/").setViewName("/toLogin");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }


    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("*")
                .allowedMethods("*")
                .maxAge(1800)
                .allowedOrigins("*");
    }

    @Bean
    public ReturnException setReturnException() {
        return new ReturnException();
    }



    // 这个方法用来注册拦截器,我们自己写好的拦截器需要通过这里添加注册才能生效
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        System.out.println("进入拦截器");
        String excludePath = elConfig.getExcludePath();
        List<String> excList = Arrays.asList(excludePath.split(","));
        //addPathPatterns是表明拦截哪些请求
        //excludePathPatterns是对哪些请求不做拦截
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excList);
    }

}

PubElConfig 配置对象

@Setter
@Getter
@Component
public class PubElConfig {

    @Value("${kafka-log-topic}")
    private String kafkaLogTopic;

    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    @Value("${excludePath}")
    private String excludePath;
}

application.yml 添加配置(顶格)

excludePath: "/api/sms/*,/api/userinfo/webLogin,/api/userinfo/registerUserInfo"

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java中的token认证可以使用JSON Web Token(JWT)。JWT是一种基于JSON的开放标准,它允许发布者在受信任的第三方之间传递受保护的声明。使用JWT,发布者可以使用密钥签名和验证声明,以确保它们不被篡改或伪造。JWT还可以用于存储安全数据,并在认证时使用。 ### 回答2: 在Java中,可以使用一些库和框架来实现token认证,最常用的是使用JWT(JSON Web Token)。 JWT是一种基于互联网标准的认证方式,可以在用户和应用程序之间安全地传递认证信息。以下是使用JWT实现token认证的基本步骤: 1. 用户登录:用户使用用户名和密码向服务器发送登录请求。服务器进行身份验证并通过后,生成一个包含用户信息和其他自定义数据的JWT。 2. 客户端存储token:服务器将生成的JWT返回给客户端,客户端将其存储在本地,通常是在浏览器的localStorage或sessionStorage中。 3. 发送token:客户端在每次请求时,将JWT通过请求头或其他方式发送给服务器。 4. 服务器验证token:服务器接收到请求后,会从请求中取出JWT,并进行验证验证的步骤包括检查JWT的签名是否正确、检查token的有效期是否过期以及检查token是否被篡改。 5. 完成认证:如果JWT验证通过,服务器会根据其中的信息,判断用户的身份和权限,并生成相应的响应。 使用JWT实现token认证的好处是它无需在服务器端保留 session 状态,每次认证都是无状态的,可以更好地支持多服务器的分布式应用。 除了JWT,还有其他方式来实现token认证,比如使用一些开源框架或者自定义token生成和验证的逻辑。总的来说,token认证是一种通用的认证方式,在Java中可以使用各种工具和方法来实现,选择合适的方式需根据实际情况和项目需求来决定。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值