精华内容
下载资源
问答
  • 1.今天遇到了一个棘手...shiro框架中如何通过用户名密码在代码中直接登录? 首先在网上找了下,找到了这种方式: Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new...

    1.今天遇到了一个棘手的问题,在此写下博客记录下来,用于提醒自己,以及帮助以后会遇到这样的问题的人

    shiro框架中如何通过用户名密码在代码中直接登录?

    首先在网上找了下,找到了这种方式:

                

    Subject currentUser = SecurityUtils.getSubject();
    				UsernamePasswordToken token = new UsernamePasswordToken(username,password, false,request.getRemoteAddr());
    				currentUser.login(token);

     

     

     

     

     

    由于自己的项目中登录时还要判断用户角色所以,用了UsernamePasswordUsertypeToken.java,代码如下

     

     

    
    
    import org.apache.shiro.authc.HostAuthenticationToken;
    import org.apache.shiro.authc.RememberMeAuthenticationToken;
    /**
     * 参考org.apache.shiro.authcUsernamePasswordToken,增加了用户类型参数
     * @author caihz
     * @see org.apache.shiro.authcUsernamePasswordToken
     */
    public class UsernamePasswordUsertypeToken implements HostAuthenticationToken, RememberMeAuthenticationToken {
    	/**
         * 用户名
         */
        private String username;
    
        /**
         * 密码, in char[] format
         */
        private char[] password;
    
        /**
         * 是否记住我
         * 默认值:<code>false</code>
         */
        private boolean rememberMe = false;
    
        /**
         * 主机名称或ip
         */
        private String host;
        
        /**
         * 用户类型
         */
        private String usertype;
    
        public UsernamePasswordUsertypeToken() {
        }
    
        /**
         * 构造方法
         * @param username 用户名
         * @param password 密码(char[])
         * @param rememberMe 是否记住我
         * @param host 主机或ip
         * @param usertype 用户类型
         */
        public UsernamePasswordUsertypeToken(final String username, final char[] password,
                                     final boolean rememberMe, final String host, final String usertype) {
    
            this.username = username;
            this.password = password;
            this.rememberMe = rememberMe;
            this.host = host;
            this.usertype = usertype;
        }
        
        /**
         * 构造方法
         * @param username 用户名
         * @param password 密码(String)
         * @param rememberMe 是否记住我
         * @param host 主机或ip
         * @param usertype 用户类型
         */
        public UsernamePasswordUsertypeToken(final String username, final String password,
                                     final boolean rememberMe, final String host, final String usertype) {
            this(username, password != null ? password.toCharArray() : null, rememberMe, host, usertype);
        }
    
    	public String getUsername() {
            return username;
        }
        public void setUsername(String username) {
            this.username = username;
        }
        public char[] getPassword() {
            return password;
        }
        public void setPassword(char[] password) {
            this.password = password;
        }
    
        /**
         * Simply returns {@link #getUsername() getUsername()}.
         *
         * @return the {@link #getUsername() username}.
         * @see org.apache.shiro.authc.AuthenticationToken#getPrincipal()
         */
        public Object getPrincipal() {
            return getUsername();
        }
    
        /**
         * Returns the {@link #getPassword() password} char array.
         *
         * @return the {@link #getPassword() password} char array.
         * @see org.apache.shiro.authc.AuthenticationToken#getCredentials()
         */
        public Object getCredentials() {
            return getPassword();
        }
        public String getHost() {
            return host;
        }
        public void setHost(String host) {
            this.host = host;
        }
        public boolean isRememberMe() {
            return rememberMe;
        }
        public void setRememberMe(boolean rememberMe) {
            this.rememberMe = rememberMe;
        }
        public String getUsertype() {
    		return usertype;
    	}
    	public void setUsertype(String usertype) {
    		this.usertype = usertype;
    	}
    
    	/**
    	 * 清除数据
    	 * 密码如果不为空,设置成0x00
    	 */
        public void clear() {
            this.username = null;
            this.host = null;
            this.rememberMe = false;
            this.usertype = null;
    
            if (this.password != null) {
                for (int i = 0; i < password.length; i++) {
                    this.password[i] = 0x00;
                }
                this.password = null;
            }
    
        }
    
        /**
         * 重写toString方法
         */
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(getClass().getName());
            sb.append(" - ");
            sb.append(username);
            sb.append(", usertype=").append(usertype);
            sb.append(", rememberMe=").append(rememberMe);
            if (host != null) {
                sb.append(" (").append(host).append(")");
            }
            return sb.toString();
        }
    
    
    }
    

    将上述代码修改了下,如下:

     

     

    Subject currentUser = SecurityUtils.getSubject();
    				UsernamePasswordUsertypeToken token = new UsernamePasswordUsertypeToken(user.getPhone(),user.getLoginPass(), false,req.getRemoteAddr(),role);
    				currentUser.login(token);


    这里controller层里的代码差不多了,接下来要修改shiro里的配置以及Realm

     

    配置如下:

     

    <!-- 和教育的Realm 2015-9-10-->
    	<bean id="shiroAndEduRealm" class="com.linkage.educloud.ucenter.learn.service.ShiroAndEduRealm">
    		<!-- property name="accountService" ref="accountService"/ -->
    		<!-- 获得的用户密码已经是加密过的了,验证的时候不用加密验证了-->
    	<!-- <property name="credentialsMatcher" ref="credentialsMatcher"/> -->
    		<property name="authenticationTokenClass" value="com.linkage.educloud.ucenter.login.shiro.UsernamePasswordUsertypeToken"/>
    	</bean>

     

     

    <bean id="authenticator" class="com.linkage.educloud.ucenter.login.shiro.FirstSuccessfulModularRealmAuthenticator">
    		<property name="authenticationStrategy" ref="firstSuccessfulStrategy"/>
    		<property name="realms">
    			<list>
                    <ref bean="shiroDbRealm"/>
                    <ref bean="shiroXxtRealm"/>
                    <ref bean="shiroAndEduRealm"/>
                </list>
    		</property>
    	</bean>

     

     

     

     

     

    com.linkage.educloud.ucenter.learn.service.ShiroAndEduRealm.java代码如下:

     

    
    
    import org.apache.shiro.authc.AccountException;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.linkage.educloud.api.ucenter.service.Uc2XXTIfaceService;
    import com.linkage.educloud.base.util.StringUtil;
    import com.linkage.educloud.domain.ucenter.login.UcenterLoginUser;
    import com.linkage.educloud.ucenter.login.service.UcenterLoginService;
    import com.linkage.educloud.ucenter.login.shiro.UsernamePasswordUsertypeToken;
    
    /**
     * 登录Realm
     * @author caihz
     *
     */
    public class ShiroAndEduRealm extends AuthorizingRealm {
    	final static Logger log = LoggerFactory.getLogger(ShiroAndEduRealm.class);
    	
    	@Autowired
    	private UcenterLoginService ucenterLoginService;
    
    	@Autowired
    	private Uc2XXTIfaceService xxtService;
    	
    	/**
    	 * 登录认证回调函数,登录时调用.
    	 * @param authcToken 登录页面参数,用户名和密码等
    	 */
    	@Override
    	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
    		UsernamePasswordUsertypeToken token = (UsernamePasswordUsertypeToken) authcToken;
    		String username = token.getUsername();
    		if (username == null){
    			throw new AccountException("账号不能为空");
    		}
    		//如果用户中心没有用户,则请求校讯通登录接口并查询用户中心对应的用户
    		String password = String.valueOf(token.getPassword());
    		//password = StringUtil.md5(password);
    		password = password.toLowerCase();
    		UcenterLoginUser user = ucenterLoginService.findUserByXxt(token.getUsername(), password, token.getUsertype());
    		if (user != null){
    			return new SimpleAuthenticationInfo(user, user.getLoginPass(), getName());
    		}else {
    			return null;
    		}
    		
    	}
    
    	/**
    	 * 登录认证通过后的权限查询函数, 由于目前用户中心前台页面不需要权限控制,所以没写,以后如果需要可扩展
    	 * 
    	 * @see org.apache.shiro.authz.AuthorizationInfo
    	 */
    	@Override
    	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    		SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    
    		return info;
    	}
    }
    


    这下可终于解决了用户直接通过token鉴权登录的问题了。

     

    例子:

     

    @RequestMapping("/test")
    	public String test(HttpServletRequest request){
    		Subject subject = SecurityUtils.getSubject();
    	       // subject.login(new UsernamePasswordToken(user.getPhone(), user.getLoginPass()));
    			//UsernamePasswordUsertypeToken up = new UsernamePasswordUsertypeToken("13816005001", "123456aA1", false, null, "3");
    			UsernamePasswordUsertypeToken up = new UsernamePasswordUsertypeToken("20212414", "4d45acd6c95faab86980ebbae7cad57c", false, request.getRemoteAddr(), "1");
    		//UsernamePasswordUsertypeToken up = new UsernamePasswordUsertypeToken("20212414", "bLaP7@3U", false, request.getRemoteAddr(), "1");    
    		subject.login(up);
    		System.out.println("登录成功!");
    		return "redirect:/ucenter/index/index";
    	}

     

     

     

     

     

     

    展开全文
  • 将查询到的权限数据保存到session中,之后可以直接从session中获取 也可以使用redisJ解决 2、代码实操 tokenVo 过期时间:方便前端人员判断是否置换,在快过期但用户又有新操作时需要置换 package com.bean.vo; ...

    @TOC

    1、基本概念

    为什么要使用token

    优点

    1. 后台不用保存token,只需要验证是否是自己签发的token
    2. 支持多种前段,如移动端和浏览器

    缺点和解决方法

    每次都要去数据库查询权限信息验证token
    解决:
    将查询到的权限数据保存到session中,之后可以直接从session中获取
    也可以使用redisJ解决

    2、代码实操

    tokenVo

    过期时间:方便前端人员判断是否置换,在快过期但用户又有新操作时需要置换

    package com.bean.vo;
    import java.io.Serializable;
    /**
     * 返回前端-Token相关VO
     */
    public class TokenVO implements Serializable {
    
    	/** 用户认证凭据 */
    	private String token;
    	/** 过期时间 */
    	//通常是总毫秒数: token生成时间+有效时长
    	private long expTime;
    	/** 生成时间 */
    	//通常是总毫秒数
    	private long genTime;
    
    	public String getToken() {
    		return token;
    	}
    	public void setToken(String token) {
    		this.token = token;
    	}
    	public long getExpTime() {
    		return expTime;
    	}
    	public void setExpTime(long expTime) {
    		this.expTime = expTime;
    	}
    	public long getGenTime() {
    		return genTime;
    	}
    	public void setGenTime(long genTime) {
    		this.genTime = genTime;
    	}
    	
    	public TokenVO() {
    		super();
    	}
    	public TokenVO(String token, long expTime, long genTime) {
    		super();
    		this.token = token;
    		this.expTime = expTime;
    		this.genTime = genTime;
    	}
    }
    

    2.1、前端

    localStorage(可以将token保存到这里)
    res.data是dto的data属性

     <script type="text/javascript">
    	  $(".submit").bind("click", function () {
    		  $.post(
    		  		"/auth/login",
    				$("#actionForm").serialize(),
    			    function (res) {
    					//判断
    					if (res.success == "true"){ //登录成功
    						//将token保存到localStorage
    						localStorage.token = res.data.token;
    						//保存token过期时间
    						localStorage.tokenExpire = res.data.expTime;
    						//跳转页面
    						location = "/page/main.html";
    					}else {
    						//显示错误提示
    						$(".info").html(res.msg);
    					}
    				}
    		  );
    	  })
      </script>
    
    package com.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class ViewController {
    
        @RequestMapping("/")
        public String base(){
            System.out.println(">>> base");
            return "/page/login.html";
        }
    }
    
    

    TokenUtil.java

    session时自动刷新,而token要前端来请求,所以token的置换剩余时间少于1个小时
    UserAgentInfo类来自于辅助工具依赖 的插件

    package com.util;
    
    import com.alibaba.fastjson.JSON;
    import cz.mallat.uasparser.UserAgentInfo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.io.IOException;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Calendar;
    import java.util.Date;
    
    @Component
    public class TokenUtil {
    
        @Autowired
        private RedisUtil redisUtil;
    
        private int session_timeout = 60 * 60 * 2; //token有效时长
    
        private int replacement_protection_timeout = 60 * 60; //token置换保护时长
    
        private int replacement_delay= 60 * 2; //旧token延迟过期时间
    
        private String token_prefix = "token:";//token前缀
    
        /** 获取token默认有效时长 */
        public int getTimeout(){
            return session_timeout;
        }
    
        /** 获取登录时间的总毫秒数 */
        public long getLogin(String token) throws ParseException {
            //拆分token
            String[] arr = token.split("[-]");
    
            return new SimpleDateFormat("yyyyMMddHHmmss").parse(arr[3]).getTime();
        }
    
        /** token是否存在 */
        public boolean exists(String token){
            return redisUtil.exists(token);
        }
    
        /**
         * 保存token
         * @param token
         * @param user
         */
        public void save(String token, Object user) {
            //判断, token是否以"token:PC-"开头
            if (token.startsWith(token_prefix + "PC-")){
                //PC端登录有效期2小时
                redisUtil.setString(token, JSON.toJSONString(user), session_timeout);
            }else {
                //移动端登录永久有效
                redisUtil.setString(token, JSON.toJSONString(user));
            }
        }
    
        /**
         * 读取token中的对象
         * @param token
         * @param clazz
         * @return
         */
        public Object load(String token, Class clazz){
            return JSON.parseObject(redisUtil.getString(token), clazz);
        }
    
        /**
         * 删除token
         * @param token
         */
        public void delete(String token) throws Exception {
            //判断, token在redis中是否存在
            if (!redisUtil.exists(token))
                throw new Exception();
            //删除token
            redisUtil.delete(token);
        }
    
        /**
         * 生成token
         * @param name 用户账号
         * @param id 用户id
         * @param agent 设备信息
         * @return token<br>
         *     格式:<br>
         *         PC端: 前缀PC-32位的加密name-id-yyyyMMddHHmmss-6位加密的agent<br>
         *         移动端: 前缀MOBLIE-32位的加密name-id-yyyyMMddHHmmss-6位加密的agent
         */
        public String generateToken(String name, Object id, String agent){
    
            StringBuilder sb = new StringBuilder();
    
            //添加token前缀
            sb.append(token_prefix);
    
            //添加设备类型
            try {
                //获取终端类型
                String deviceType = getDeviceType(agent);
                //添加到token
                sb.append(deviceType + "-");
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
    
            //添加账号(32位加密)
            sb.append(MD5.getMd5(name, 32) + "-");
    
            //添加id
            sb.append(id + "-");
    
            //添加登录时间, 格式yyyyMMddHHmmss
            sb.append(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + "-");
    
            //添加设备信息(6加密)
            sb.append(MD5.getMd5(agent, 6));
    
            return sb.toString();
        }
    
        /**
         * 验证token
         * @param token
         * @param agent 设备信息
         * @param type 验证类型<br>
         *     1: 基本验证. 包含结果1,11,12,13,20
         *     2: 置换验证. 包含结果1,11,12,14
         * @return 结果数值<br>
         *     11: token不存在<br>
         *     12: token格式有误<br>
         *     13: token已超时<br>
         *     14: token仍在保护期<br>
         *     20: 其它token异常<br>
         *     1: token正常
         */
        public int validate(String token, String agent, String type){
            // token不存在
            if (!exists(token)) return 11;
            //拆分token字符串
            String[] tokenDetails = token.split("-");
            //获取token生成时间的总毫秒数
            long genTime = 0;
            try {
               genTime = getLogin(token);
            } catch (ParseException e) {
                e.printStackTrace();
                return 12;
            }
            //计算登录时长
            long passed = Calendar.getInstance().getTimeInMillis() - genTime;
            System.out.println(passed);
            System.out.println(passed / 1000 / 60);
            //判断, 验证类型
            if (type.equals("1")){
                //判断, 是否已超时
                if (passed > session_timeout * 1000) return 13;
                //获取登录设备加密信息
                String agentMD5 = tokenDetails[4];
                //判断, 当前设备是否与登录设备一致
                if(MD5.getMd5(agent, 6).equals(agentMD5)) return 1;
            }else {
                //判断, 是否处于置换保护期内
                if (passed < replacement_protection_timeout * 1000) return 14;
                return 1;
            }
    
            return 20;
        }
    
        /**
         * 置换token
         * @param token
         * @param agent 设备信息
         * @param clazz 置换对象的类型
         * @param nameFieldName 账号属性名
         * @param idFiledName id属性名
         * @return
         */
        public String replaceToken(String token, String agent, Class clazz, String nameFieldName, String idFiledName) {
            //获取旧token中存储的用户数据
            Object user = load(token, clazz);
            System.out.println(user);
    
            //获取旧token有效期(剩余秒数 )
            long ttl = redisUtil.getExpire(token);
    //        System.out.println("ttl:" + ttl);
            //判断token是否仍在有效期内容
    //        if (ttl > 0 || ttl == -1) {
                Object name = ""; //用户账号
                Object id = ""; //用户id
                try {
                    //获取用户账号
                    Method getName = clazz.getDeclaredMethod(nameFieldName);
                    name = getName.invoke(user);
                    //获取用户id
                    Method getId = clazz.getDeclaredMethod(idFiledName);
                    id = getId.invoke(user);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
                //生成新token
                String newToken = this.generateToken(name.toString(), id.toString(), agent);
                //保存新token
                this.save(newToken, user);
                //设置2分钟后旧token过期,注意手机端由永久有效变为2分钟后失效
                redisUtil.setString(token, JSON.toJSONString(user), replacement_delay);
    
                return newToken;
    //        }
    
    //        return null;
        }
    
        /**
         * 获取设备类型
         * @param agent
         * @return
         */
        public String getDeviceType(String agent) throws IOException {
            //用户设备信息对象
            UserAgentInfo userAgentInfo = UserAgentUtil.getUasParser().parse(agent);
            //判断, 设备类型是否未知
            if (userAgentInfo.getDeviceType().equals(UserAgentInfo.UNKNOWN)) {
                //判断, 已知设备类型是否为移动端
                if (UserAgentUtil.CheckAgent(agent)) return "MOBILE";
                //已知设备不是移动端, 则为PC端
                else return "PC";
            }
            //判断, 设备类型是否为PC端
            else if (userAgentInfo.getDeviceType().equals("Personal computer")) return "PC";
            //其他类型均识别为移动端
            else return "MOBILE";
        }
    }
    
    

    控制类

    登录方法

     @PostMapping("/login")
        @ResponseBody
        public Dto login(User user, HttpServletRequest request){
            System.out.println(">>> 用户登录");
            System.out.println(user);
    
            /* 数据验证(略) */
    
            /* 登录查询 */
            List<User> userList = userService.find(user);
    
            /* 处理查询结果 */
            //判断, 集合大小
            if (userList.size() != 1)
                return new Dto("用户名或密码错误!", "100001");
    
            //获取登录用户对象
            user = userList.get(0);
    
            //判断, 账号状态
            if (user.getStatus() == 0)
                return new Dto("账号未激活, 请先激活账号!", "100002");
    
            //登录成功,就要生成token
            String token = tokenUtil.generateToken(user.getUserName(), user.getId(),
                    request.getHeader("user-agent"));
    
            //保存token
            tokenUtil.save(token, user);
    
            try {
                //获取tokenVo
                TokenVO tokenVO = new TokenVO(token,
                        tokenUtil.getLogin(token) + tokenUtil.getTimeout()*1000,
                        tokenUtil.getLogin(token));
    
                return new Dto(tokenVO);
    
            } catch (ParseException e) {
                e.printStackTrace();
                return new Dto("token格式有误!", "100003");
            }
    
        }
    
        @PostMapping("/registe/mail")
        @ResponseBody
        public Dto registeByEmail(User user){
            System.out.println(">>> 用户注册--邮箱");
            System.out.println(user);
    
            //添加用户
            userService.addUserByMail(user);
    
            return new Dto("注册成功, 请尽快激活账号!");
        }
    

    启动启动类
    启动nginx和redis
    就可以访问了

    获取header中的token

    有时候我们登录之后要去token中拿数据
    在这里插入图片描述

     @GetMapping("/login/info")
        @ResponseBody
        public Dto getLoginInfo(HttpServletRequest request){
            System.out.println(">>> 获取登录用户信息");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, request.getHeader("user-agent"), "1");
    
            //根据验证结果的所有异常
            switch (result){
                case 11: 
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 13:
                    return new Dto("token已超时", "200013");
                case 20:
                    return new Dto("token异常", "200020");
            }
    
            //获取token中的对象
            User user = (User) tokenUtil.load(token, User.class);
    
            //返回
            return new Dto(user);
        }
    

    前端
    每次需要登录向后台请求都要带上 headers

    	  /* 加载登录用户信息 */
    	  $.ajax({
    	     type: "get",
    		 url: "/auth/login/info",
    		 success: function (res) {
    			 if (res.success == "true") {
    				 $("#loginUserName").html(res.data.realName);
    			 }else {
    				 $("#loginUserName").html("游客(未登录)");
    			 }
    		 },
    	     headers: {
    		 	token: localStorage.token
    		 }
    	  });
    

    置换token,退出

    置换

    前端需要判断token是否过期,如果过期就需要置换(如果需要登录才能访问的情况下)

    前端

    	 function parseToken(token){
    		  //拆分token
    		  var arr = token.split("-");
    		  //获取年
    		  var year = arr[3].substring(0, 4);
    		  //获取月
    		  var month = arr[3].substring(4, 6);
    		  //获取日
    		  var date = arr[3].substring(6, 8);
    		  //获取时
    		  var hours = arr[3].substring(8, 10);
    		  //获取分
    		  var min = arr[3].substring(10, 12);
    		  //获取秒
    		  var sec = arr[3].substring(12);
    
    		  return new Date(year, month-1, date, hours, min, sec);
    		  // alert(d);
    	  }
    
    	  replaceToken();
    
    	  //置换token
    	  function replaceToken() {
    		  //获取token
    		  var token = localStorage.token;
    		  //获取过期时间
    		  var expTime = localStorage.tokenExpire;
    		  // alert(expTime)
    		  // var date = parseToken(token);
    
    		  //获取剩余登录时间的毫秒数
    		  var loginTimeLeft =  expTime - new Date().getTime();
    		  // alert(loginTimeLeft < 1000 * 60 * 60);
    		  //判断, 是否需要置换token
    		  if (loginTimeLeft < 1000 * 60 * 60){
    		  	$.ajax({
    				headers: {
    					token: localStorage.token
    				},
    				type: "get",
    				url: "/auth/token/replace",
    				success: function (res) {
    					if (res.success == "true"){
    						localStorage.token = res.data.token;
    						localStorage.tokenExpire = res.data.expTime;
    					}
    					// else{
    					// 	alert(res.msg);
    					// }
    				}
    			});
    		  }
    	  }
    
    

    控制类

     @GetMapping("/token/replace")
        @ResponseBody
        public Dto replaceToken(HttpServletRequest request){
            System.out.println(">>> 置换token");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, null,"2");
    
            try {
    //            System.out.println(tokenUtil.getLogin(token));
                System.out.println(new Date(tokenUtil.getLogin(token)));
                System.out.println(tokenUtil.getTimeout());
            } catch (ParseException e) {
                e.printStackTrace();
            }
    
            //根据验证结果的所有异常
            switch (result){
                case 11:
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 14:
                    return new Dto("token已超时", "200013");
            }
    
            //置换token, 获取新token
            token = tokenUtil.replaceToken(token, request.getHeader("user-agent"),
                    User.class, "userName", "id");
    
            //判断, 新token
            if (token == null)
                return new Dto("token置换失败!", "200030");
    
    
    
            try {
                TokenVO tokenVO = new TokenVO(token,
                        tokenUtil.getLogin(token) + tokenUtil.getTimeout()*1000,
                        tokenUtil.getLogin(token));
    
                return new Dto(tokenVO);
    
            } catch (ParseException e) {
                e.printStackTrace();
                return new Dto("token格式有误!", "100003");
            }
        }
    
        @GetMapping("/login/info")
        @ResponseBody
        public Dto getLoginInfo(HttpServletRequest request){
            System.out.println(">>> 获取登录用户信息");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //验证token是否正常
            int result = tokenUtil.validate(token, request.getHeader("user-agent"), "1");
    
            //根据验证结果的所有异常
            switch (result){
                case 11:
                    return new Dto("token不存在", "200011");
                case 12:
                    return new Dto("token格式有误", "200012");
                case 13:
                    return new Dto("token已超时", "200013");
                case 20:
                    return new Dto("token异常", "200020");
            }
    
            //获取token中的对象
            User user = (User) tokenUtil.load(token, User.class);
    
            //返回
            return new Dto(user);
        }
    

    退出

    验证,是否是我记录的token,以及是否是同个设备
    前端

     <a href="javascript:void(0)" id="logoff">退出</a>
     function replaceToken() {
    		
    	  $("#logoff").bind("click", function () {
    
    	  	if (!confirm("确定退出系统吗?")) return;
    
    		$.ajax({
    			headers: {
    				token: localStorage.token
    			},
    		  	url: "/auth/logoff",
    			type: "get",
    			success: function (res) {
    				if (res.success == "true") {
    				#清除
    					localStorage.token = null;
    					localStorage.tokenExpire = null;
    					location = "/page/login.html";
    				}
    			}
    		});
      })
    

    后台

     @GetMapping("/logoff")
        @ResponseBody
        public Dto logoff(HttpServletRequest request){
            System.out.println(">>> 退出登录");
    
            //获取header中的token
            String token = request.getHeader("token");
            System.out.println(token);
    
            //删除token
            try {
                tokenUtil.delete(token);
            } catch (Exception e) {
                e.printStackTrace();
                return new Dto("token不存在!", "100011");
            }
    
            return new Dto("退出成功!");
        }
    
    展开全文
  • SpringBoot整合token实现登录认证

    万次阅读 多人点赞 2019-06-14 17:43:32
    最近在做登录认证的时候,遇到了验证的问题,如何用token验证困扰了许久,所有在网上查询了许多博客,总结了一下,希望能帮助到大家,好了,废话不多说,直接上代码

    标题SpringBoot整合JWT实现登录认证

    1.pom.xml

    <dependencies>
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-web</artifactId>
    		</dependency>
    
    		<dependency>
    			<groupId>org.springframework.boot</groupId>
    			<artifactId>spring-boot-starter-test</artifactId>
    			<scope>test</scope>
    		</dependency>
    		
    		<dependency>
    		      <groupId>com.auth0</groupId>
    		      <artifactId>java-jwt</artifactId>
    		      <version>3.4.0</version>
    		</dependency>
    		
    		 <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.2</version>
            </dependency>
            
    		 <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.47</version>
            </dependency>
    
    		<dependency>
    			<groupId>io.springfox</groupId>
    			<artifactId>springfox-swagger2</artifactId>
    			<version>2.8.0</version>
    		</dependency>
    		<dependency>
    			<groupId>io.springfox</groupId>
    			<artifactId>springfox-swagger-ui</artifactId>
    			<version>2.8.0</version>
    		</dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.0</version>
            </dependency>
    
        </dependencies>
    
    

    2.实体类

    @Data
    public class User {
    	private String id;
        private String username;
        private String password;
    }
    

    3.Mapper接口

    /**
     * @author qiaoyn
     * @date 2019/06/14
     */
    @Mapper
    public interface UserMapper {
    	
        User findByUsername(String username);
        
        User findUserById(String id);
    
    }
    

    4.service层

    @Service
    public class UserService {
        @Autowired
        private UserMapper userMapper;
        public User findByUsername(User user){
            return userMapper.findByUsername(user.getUsername());
        }
        public User findUserById(String userId) {
            return userMapper.findUserById(userId);
        }
    }
    
    /***
     * token 下发
    * @Title: TokenService.java 
    * @author qiaoyn
    * @date 2019/06/14
    * @version V1.0
     */
    @Service
    public class TokenService {
    
    	public String getToken(User user) {
    		Date start = new Date();
    		long currentTime = System.currentTimeMillis() + 60* 60 * 1000;//一小时有效时间
    		Date end = new Date(currentTime);
    		String token = "";
    		
    		token = JWT.create().withAudience(user.getId()).withIssuedAt(start).withExpiresAt(end)
    				.sign(Algorithm.HMAC256(user.getPassword()));
    		return token;
    	}
    }
    
    

    5.Api层

    @RestController
    public class UserApi {
    	@Autowired
    	UserService userService;
    	@Autowired
    	TokenService tokenService;
    
    	// 登录
    	@ApiOperation(value = "登陆", notes = "登陆")
    	@RequestMapping(value = "/login" ,method = RequestMethod.GET)
    	public Object login(User user, HttpServletResponse response) {
    		JSONObject jsonObject = new JSONObject();
    		User userForBase = new User();
    		userForBase.setId(userService.findByUsername(user).getId());
    		userForBase.setUsername(userService.findByUsername(user).getUsername());
    		userForBase.setPassword(userService.findByUsername(user).getPassword());
    		if (!userForBase.getPassword().equals(user.getPassword())) {
    			jsonObject.put("message", "登录失败,密码错误");
    			return jsonObject;
    		} else {
    			String token = tokenService.getToken(userForBase);
    			jsonObject.put("token", token);
    
    			Cookie cookie = new Cookie("token", token);
    			cookie.setPath("/");
    			response.addCookie(cookie);
    
    			return jsonObject;
    
    		}
    	}
    	/***
    	 * 这个请求需要验证token才能访问
    	 * 
    	 * @author: qiaoyn
    	 * @date 2019/06/14
    	 * @return String 返回类型
    	 */
    	@UserLoginToken
    	@ApiOperation(value = "获取信息", notes = "获取信息")
    	@RequestMapping(value = "/getMessage" ,method = RequestMethod.GET)
    	public String getMessage() {
    
    		// 取出token中带的用户id 进行操作
    		System.out.println(TokenUtil.getTokenUserId());
    
    		return "您已通过验证";
    	}
    }
    
    

    6.util

    /* 
    * @author qiaoyn
    * @date 2019/06/14
    * @version 1.0 
    */
    public class TokenUtil {
    
    	public static String getTokenUserId() {
    		String token = getRequest().getHeader("token");// 从 http 请求头中取出 token
    		String userId = JWT.decode(token).getAudience().get(0);
    		return userId;
    	}
    
    	/**
    	 * 获取request
    	 * 
    	 * @return
    	 */
    	public static HttpServletRequest getRequest() {
    		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
    				.getRequestAttributes();
    		return requestAttributes == null ? null : requestAttributes.getRequest();
    	}
    }
    
    

    7.Interceptor

    /**
     * 拦截器
     * @author qiaoyn
     * @date 2019/06/14
     */
    public class AuthenticationInterceptor implements HandlerInterceptor {
        @Autowired
        UserService userService;
        @Override
        public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
            String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
            // 如果不是映射到方法直接通过
            if(!(object instanceof HandlerMethod)){
                return true;
            }
            HandlerMethod handlerMethod=(HandlerMethod)object;
            Method method=handlerMethod.getMethod();
            //检查是否有passtoken注释,有则跳过认证
            if (method.isAnnotationPresent(PassToken.class)) {
                PassToken passToken = method.getAnnotation(PassToken.class);
                if (passToken.required()) {
                    return true;
                }
            }
            //检查有没有需要用户权限的注解
            if (method.isAnnotationPresent(UserLoginToken.class)) {
                UserLoginToken userLoginToken = method.getAnnotation(UserLoginToken.class);
                if (userLoginToken.required()) {
                    // 执行认证
                    if (token == null) {
                        throw new RuntimeException("无token,请重新登录");
                    }
                    // 获取 token 中的 user id
                    String userId;
                    try {
                        userId = JWT.decode(token).getAudience().get(0);
                    } catch (JWTDecodeException j) {
                        throw new RuntimeException("401");
                    }
                    User user = userService.findUserById(userId);
                    if (user == null) {
                        throw new RuntimeException("用户不存在,请重新登录");
                    }
                    // 验证 token
                    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
                    try {
                        jwtVerifier.verify(token);
                    } catch (JWTVerificationException e) {
                        throw new RuntimeException("401");
                    }
                    return true;
                }
            }
            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 {
    
        }
    }
    
    

    8.cofig

    /***
     * 新建Token拦截器
    * @Title: InterceptorConfig.java 
    * @author qiaoyn
    * @date 2019/06/14
    * @version V1.0
     */
    @Configuration
    public class InterceptorConfig implements WebMvcConfigurer {
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(authenticationInterceptor())
                    .addPathPatterns("/**");    // 拦截所有请求,通过判断是否有 @LoginRequired 注解 决定是否需要登录
        }
        @Bean
        public AuthenticationInterceptor authenticationInterceptor() {
            return new AuthenticationInterceptor();
        }
    	@Override
    	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void addCorsMappings(CorsRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void addFormatters(FormatterRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void addResourceHandlers(ResourceHandlerRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void addViewControllers(ViewControllerRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureAsyncSupport(AsyncSupportConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureContentNegotiation(ContentNegotiationConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configurePathMatch(PathMatchConfigurer arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void configureViewResolvers(ViewResolverRegistry arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public void extendMessageConverters(List<HttpMessageConverter<?>> arg0) {
    		// TODO Auto-generated method stub
    		
    	}
    	@Override
    	public MessageCodesResolver getMessageCodesResolver() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    	@Override
    	public Validator getValidator() {
    		// TODO Auto-generated method stub
    		return null;
    	}
    	
    }
    
    

    9.annotation

    /***
     * 用来跳过验证的 PassToken
     * @author qiaoyn
     * @date 2019/06/14
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface PassToken {
        boolean required() default true;
    }
    
    /**
     * 用于登录后才能操作的token
     * @author qiaoyn
     * @date 2019/06/14
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface UserLoginToken {
        boolean required() default true;
    }
    
    /*RetentionPolicy.RUNTIME:这种类型的Annotations将被JVM保留,
    所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用。*/
    

    10.mapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
    <mapper namespace="com.example.demo.mapper.UserMapper">
        <select id="findByUsername" resultType="com.example.demo.entity.User">
          SELECT id,password
          FROM user
          WHERE
          username=#{username}
        </select>
        <select id="findUserById" resultType="com.example.demo.entity.User">
            SELECT username,password
            FROM user
            WHERE
            id=#{id}
        </select>
    
    </mapper>
    

    11.测试
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    数据库文件如下所示
    在这里插入图片描述
    好了,今天分享就到此了。

    展开全文
  • 后续客户端的每次请求资源都必须携带token,这里放在请求头中,服务端接收到请求首先校验是否携带token,以及token是否和redis中的匹配,若不存在或不匹配直接拦截返回错误信息(如未认证)。 token管理:生成、...

    设计思路

    1. 用户发出登录请求,带着用户名和密码到服务器经行验证,服务器验证成功就在后台生成一个token返回给客户端
    2. 客户端将token存储到cookie中,服务端将token存储到redis中,可以设置存储token的有效期。
    3. 后续客户端的每次请求资源都必须携带token,这里放在请求头中,服务端接收到请求首先校验是否携带token,以及token是否和redis中的匹配,若不存在或不匹配直接拦截返回错误信息(如未认证)。
    • token管理:生成、校验、解析、删除

    • token:这里使用userId_UUID的形式

    • 有效期:使用Redis key有效期设置(每次操作完了都会更新延长有效时间)

    • 销毁token:删除Redis中key为userId的内容

    • token存储:客户端(Cookie)、服务端(Redis)

    • Cookie的存取操作(jquery.cookie插件)

    • Redis存取(StringRedisTemplate)

    实现

    完整代码可到springboot2.x整合redis实现简单的token登录鉴权中下载
    (本demo只是个小例子,还有很多不足,用于实际环境还需要根据实际考虑更多的问题)

    【Redis操作类】

    package com.bpf.tokenAuth.utils;
    
    import java.util.concurrent.TimeUnit;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.stereotype.Component;
    
    @Component
    public class RedisClient {
        
        public static final long TOKEN_EXPIRES_SECOND = 1800;
    
        @Autowired
        private StringRedisTemplate redisTpl;
        
        /**
         * 向redis中设值
         * @param key 使用 a:b:id的形式在使用rdm进行查看redis情况时会看到分层文件夹的展示形式,便于管理
         * @param value
         * @return
         */
        public boolean set(String key, String value) {
            boolean result = false;
            try {
                redisTpl.opsForValue().set(key, value);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        
        
        /**
         * 向redis中设置,同时设置过期时间
         * @param key
         * @param value
         * @param time
         * @return
         */
        public boolean set(String key, String value, long time) {
            boolean result = false;
            try {
                redisTpl.opsForValue().set(key, value);
                expire(key, time);
                result =  true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        
        /**
         * 获取redis中的值
         * @param key
         * @return
         */
        public String get(String key) {
            String result = null;
            try {
                result = redisTpl.opsForValue().get(key);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
      
        }
        
        /**
         * 设置key的过期时间
         * @param key
         * @param time
         * @return
         */
        public boolean expire(String key, long time) {
            boolean result = false;
            try {
                if(time > 0) {
                    redisTpl.expire(key, time, TimeUnit.SECONDS);
                    result = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
        
        /**
         * 根据key删除对应value
         * @param key
         * @return
         */
        public boolean remove(String key) {
            boolean result = false;
            try {
                redisTpl.delete(key);
                result = true;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }
    

    【Token管理类】

    package com.bpf.tokenAuth.utils.token;
    
    import java.util.UUID;
    
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import com.bpf.tokenAuth.utils.RedisClient;
    
    @Component
    public class RedisTokenHelp implements TokenHelper {
        
        @Autowired
        private RedisClient redisClient;
    
        @Override
        public TokenModel create(Integer id) {
            String token = UUID.randomUUID().toString().replace("-", "");
            TokenModel mode = new TokenModel(id, token);
            redisClient.set(id == null ? null : String.valueOf(id), token, RedisClient.TOKEN_EXPIRES_SECOND);
            return mode;
        }
    
        @Override
        public boolean check(TokenModel model) {
            boolean result = false;
            if(model != null) {
                String userId = model.getUserId().toString();
                String token = model.getToken();
                String authenticatedToken = redisClient.get(userId);
                if(authenticatedToken != null && authenticatedToken.equals(token)) {
                    redisClient.expire(userId, RedisClient.TOKEN_EXPIRES_SECOND);
                    result = true;
                }
            }
            return result;
        }
    
        @Override
        public TokenModel get(String authStr) {
            TokenModel model = null;
            if(StringUtils.isNotEmpty(authStr)) {
                String[] modelArr = authStr.split("_");
                if(modelArr.length == 2) {
                    int userId = Integer.parseInt(modelArr[0]);
                    String token = modelArr[1];
                    model = new TokenModel(userId, token);
                }
            }
            return model;
        }
    
        @Override
        public boolean delete(Integer id) {
            return redisClient.remove(id == null ? null : String.valueOf(id));
        }
    
    }
    

    【拦截器逻辑】

    package com.bpf.tokenAuth.interceptor;
    
    import java.lang.reflect.Method;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.method.HandlerMethod;
    import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
    
    import com.bpf.tokenAuth.annotation.NoneAuth;
    import com.bpf.tokenAuth.constant.NormalConstant;
    import com.bpf.tokenAuth.entity.JsonData;
    import com.bpf.tokenAuth.utils.JsonUtils;
    import com.bpf.tokenAuth.utils.token.TokenHelper;
    import com.bpf.tokenAuth.utils.token.TokenModel;
    
    @Component
    public class LoginInterceptor extends HandlerInterceptorAdapter {
        
        @Autowired
        private TokenHelper tokenHelper;
        
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                throws Exception {
            System.out.println(11);
            // 如果不是映射到方法直接通过
            if (!(handler instanceof HandlerMethod)) {
                return true;
            }
            //如果被@NoneAuth注解代表不需要登录验证,直接通过
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            if(method.getAnnotation(NoneAuth.class) != null) return true;       
            //token验证
            String authStr = request.getHeader(NormalConstant.AUTHORIZATION);
            TokenModel model = tokenHelper.get(authStr);
            
            //验证通过
            if(tokenHelper.check(model)) {
                request.setAttribute(NormalConstant.CURRENT_USER_ID, model.getUserId());
                return true;
            }
            //验证未通过
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().write(JsonUtils.obj2String(JsonData.buildError(401, "权限未认证")));
            return false;
        }
    }
    

    【登录逻辑】

    package com.bpf.tokenAuth.controller;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.DeleteMapping;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.bpf.tokenAuth.annotation.NoneAuth;
    import com.bpf.tokenAuth.constant.MessageConstant;
    import com.bpf.tokenAuth.constant.NormalConstant;
    import com.bpf.tokenAuth.entity.JsonData;
    import com.bpf.tokenAuth.entity.User;
    import com.bpf.tokenAuth.enums.HttpStatusEnum;
    import com.bpf.tokenAuth.mapper.UserMapper;
    import com.bpf.tokenAuth.utils.token.TokenHelper;
    import com.bpf.tokenAuth.utils.token.TokenModel;
    
    @RestController
    @RequestMapping("/token")
    public class TokenController {
        
        @Autowired
        private UserMapper userMapper;
        
        @Autowired
        private TokenHelper tokenHelper;
        
        @NoneAuth
        @GetMapping
        public Object login(String username, String password) {
            User user = userMapper.findByName(username);
            if(user == null || !user.getPassword().equals(password)) {
                return JsonData.buildError(HttpStatusEnum.NOT_FOUND.getCode(), MessageConstant.USERNAME_OR_PASSWORD_ERROR);
            }
            //用户名密码验证通过后,生成token
            TokenModel model = tokenHelper.create(user.getId());
            return JsonData.buildSuccess(model);    
        }
        
        @DeleteMapping
        public Object logout(HttpServletRequest request) {
            Integer userId = (Integer) request.getAttribute(NormalConstant.CURRENT_USER_ID);
            if(userId != null) {
                tokenHelper.delete(userId);
            }
            return JsonData.buildSuccess();
        }
    
    }
    

    测试

    【login.html】


    <!DOCTYPE html>
    <html>  
    <head>
    <title>Login</title>
    <link rel="stylesheet" href="../res/css/login.css">
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
    </head>
    <body>
        <form>
            <input type="text" name="username" id="username">
            <input type="password" name="password" id="password">
        </form>
        <input type="button" value="Login" onclick="login()">
    </body>
    <script type="text/javascript">
    function login(){
        $.ajax({
            url: "/tokenAuth/token",
            dataType: "json",
            data: {'username':$("#username").val(), 'password':$("#password").val()},
            type:"GET",
            success:function(res){
                console.log(res);
                if(res.code == 200){
                    var authStr = res.data.userId + "_" + res.data.token;
                    //把生成的token放在cookie中
                    $.cookie("authStr", authStr);
                    window.location.href = "index.html";
                }else alert(res.msg);
            }
        });
    }
    </script>
    </html>
    

    【index.html】

    <!DOCTYPE html>
    <html>  
    <head>
    <title>Index</title>
    <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
    <script src="https://cdn.bootcss.com/jquery-cookie/1.4.1/jquery.cookie.js"></script>
    </head>
    <body>
        <input type="button" value="Get" onclick="get()">
        <input type="button" value="logout" onclick="logout()">
    </body>
    <script type="text/javascript">
    
    function get(){
        $.ajax({
            url: "/tokenAuth/user/bpf",
            dataType: "json",   
            type:"GET",
            beforeSend: function(request) {
                //将cookie中的token信息放于请求头中
                request.setRequestHeader("authStr", $.cookie('authStr'));
            },
            success:function(res){
                console.log(res);
            }
        });
    }
    
    function logout(){
        $.ajax({
            url: "/tokenAuth/token",
            dataType: "json",   
            type:"DELETE",
            beforeSend: function(request) {
                //将cookie中的token信息放于请求头中
                request.setRequestHeader("authStr", $.cookie('authStr'));
            },
            success:function(res){
                console.log(res);
            }
        });
    }
    </script>
    </html>
    

    测试环境中两个页面login.html和index.html均当做静态资源处理
    【未登录状态】

    【登录状态】

    • 访问登录网站http://localhost:8080/tokenAuth/page/login.html,输入username和password进行点击Login按钮登录
    • 登录成功并跳转到index页面,并且生成cookie,这里没有设置cookie有效期,默认关闭浏览器失效


    • 再次点击get按钮请求数据,请求成功


    • 点击logout按钮销毁登录状态,然后再次请求数据


    展开全文
  • 判断如果没有获取token就只能返回登录 在先router.js中写 router.beforeEach((to, from, next) => { //判断是否有token var token = localStorage.getItem("token") if (to.name === "Login") { //这里判断...
  • 最后在外网查到有人在获取token时使用了split,才注意到直接从请求头拿到的token打印出来是带双引号的,而这个双引号也被当做token的一部分进行验证了 把头尾的双引号去掉再做验证就成功了 ...
  • Token登录验证

    千次阅读 2018-05-28 21:43:57
    首先,引入crypto-js 或者测试直接使用下面的(用浏览器打开下面链接,复制到JS文件里,用script src的方式引到HTML中,避免链接失效)   &lt;script src="...&gt;...
  • vue获取token登录的过程

    千次阅读 2020-03-09 22:26:31
    vue获取token登录的过程: 1,访问需要登录的页面的时候,利用路由守卫跳转,输入正确的用户名密码 向后台的认证接口发送数据 ...当token过期返回401跳转登陆重新获取token进行登录,下面直接上代码 mian.js ```jav...
  • idea使用token登录github

    2021-02-28 00:34:42
    一、问题描述 ...之前也试过其他方法,但是当使用token后,发现token真的很方便,所以总结记录 1.github中获取token Settings->Developer settings->Personal access tokens->Generate new token N
  • 这里不具体讲解JWT是什么,有什么应用场景,只是一个简单的小Demo,可以直接复制到eclipse上直接运行的,后续文章中再来详细介绍下JWT的优势和应用场景以及在单点登录上的应用。 导入jar包 <dependency> ...
  • Java令牌Token登录与退出

    千次阅读 2020-05-25 20:34:15
    Java令牌Token登录与退出 Token 之前的博客已经介绍了各种登录的方式,现在直接介绍一种现在比较流行的登录方式,无状态登录,只需要客户端携带令牌就能登陆,服务器不再存储登录状态。突然粉丝量爆棚,开心死了,...
  • 1.场景还原可能还有很多小伙伴对token概念朦朦胧胧,今天笔者以项目中的用户登录token验证需求跟大家讲讲其中的来龙去脉,希望能够理清大伙的思路。2.需求分析这个需求可能早已是老生常谈,但我觉得它永远也不会...
  • 基于上篇文章,我想能不能不登录直接获得token的值呢,下面是我的实现方法;基于此办法可以实现不登录获得接口相关数据 二、实现过程 1.shiro配置 在web模块的application.yml文件中,找到如下内容,启用如下配置,...
  • 用户登录token验证

    万次阅读 多人点赞 2017-06-13 22:10:01
    可能还有很多小伙伴对token概念朦朦胧胧,今天笔者以项目中的用户登录token验证需求跟大家讲讲其中的来龙去脉,希望能够理清大伙的思路。 2.需求分析 这个需求可能早已是老生常谈,但我觉得它永远也不会过时 ①...
  • 基于token登录验证基于token登录验证实现1. Redis数据库 存储token2. `JWT` 产生token3. 实现登录验证基于Session的登录验证 基于token登录验证 token(令牌) ...登录的信息仅需一个key-value,如果直接
  • 我想通过token做一个自动登陆,直接跳转主页,但是我这退出登录后,再登录界面出现闪屏的现象,该如何处理???
  • postman登录接口获取token

    千次阅读 2018-12-27 11:35:36
    问题描述 登录接口中带有token参数,其他接口需要带上token才能正确访问,利用接口查询用户信息时手动在...当其他接口需要用到token时,直接引用该变量即可。 详细步骤 1.在登录接口的tests中填入如下内容,点击s...
  • pt1:方法一简单介绍思路吧。由于toten这个机制具有登录后再次登录变化的问题;...先登录你可以在network f12 下或者抓包看到登录后返回的token;不多说了比较简单 # coding:utf-8 import requests import os ...
  • 自定义Token的CAS登录

    2019-01-25 22:12:00
    工作中实际遇到的需求,我们有一个旧系统,用了CAS的单点登录,现在有一个外部系统,准备从它那里单点进来,这个外部系统提供了一个token参数来标记这是哪一个用户,我们用他们提供的方式解析出对应的用户,以这个...
  • 问题描述:有一个登录接口获取token,其他接口再次访问时都要带上token ...步骤二:访问其他接口时token直接读取变量即可。postman获取变量的语法为{{变量名}} 具体步骤: 步骤一:登录接口请求体等信息填写 ...
  • uniapp token登录效验的思路

    千次阅读 2020-04-27 16:59:02
    http请求封装:写一个中间件,每次请求都携带token进行效验,拦截后台返回的相关错误,返回200直接输出结果,返回其他错误跳转Login。 vuex封装: actions 多数用于写接口的请求。分别是请求退出,请求token , ...
  • /***单点登录(如已经登录,则直接跳转)*@paramuserCode登录用户编码*@paramtoken登录令牌,令牌组成:sso密钥+用户名+日期,进行md5加密,举例:*StringsecretKey=Global.getConfig("shiro.sso.secretKey");...
  • 往浏览器的输入框中直接输入其他页面的url则直接跳转至登录页面,进行登录token过期提醒: token有效期为24小时,若24小时之后,必须重新登录来获取新的token值。 2. 修改头部导航栏 将 <a ...>全部功能<...
  • token过期自动跳转到登录页面

    千次阅读 2020-03-17 14:02:45
    这几天项目提测,测试给我提了个bug,说...2:每次路由跳转都会对token进行判断,设置了一个全局的beforeEach钩子函数,如果token存在就跳到你所需要的页面,否则就直接跳转到登录页面,让用户登录重新存取token { ...
  • 当浏览器不支持cookie的时候,我们就无法直接使用session。在这种情况下,用token可以达到相同的目的。 Token中文译名是令牌,是服务端生成的一串字符串,作为客户端进行请求的一个标识。当后台接收到用户发来的请求...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,342
精华内容 536
热门标签
关键字:

token直接登录