• shiro token 分析


    1.ShiroConfig.java 定义匿名用户可以访问的资源

      filterMap.put("/webjars/**", "anon");
            filterMap.put("/druid/**", "anon");
            filterMap.put("/api/**", "anon");
            filterMap.put("/sys/login", "anon");
            filterMap.put("/**/*.css", "anon");
            filterMap.put("/**/*.js", "anon");
            filterMap.put("/**/*.html", "anon");
            filterMap.put("/fonts/**", "anon");
            filterMap.put("/plugins/**", "anon");
            filterMap.put("/swagger/**", "anon");
            filterMap.put("/favicon.ico", "anon");
            filterMap.put("/", "anon");
            filterMap.put("/**", "oauth2");           --除了anon,拦截其他所有请求

    2.OAuth2Filter.java 基于shiro的全局过滤器

    继承AuthenticatingFilter 实现createToken、isAccessAllowed、onAccessDenied、onLoginFailure等抽象方法

    import com.google.gson.Gson;
    import io.renren.common.utils.R;
    import org.apache.commons.lang.StringUtils;
    import org.apache.http.HttpStatus;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    public class OAuth2Filter extends AuthenticatingFilter {

        @Override
        protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token
            String token = getRequestToken((HttpServletRequest) request);

            if(StringUtils.isBlank(token)){
                return null;
            }
            return new OAuth2Token(token);
        }

        @Override
        protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
            return false;
        }

        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            //获取请求token,如果token不存在,直接返回401
            String token = getRequestToken((HttpServletRequest) request);
            if(StringUtils.isBlank(token)){
                HttpServletResponse httpResponse = (HttpServletResponse) response;
                String json = new Gson().toJson(R.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));
                httpResponse.getWriter().print(json);
                return false;
            }
            System.out.println("onAccessDenied-----------------------onAccessDenied");
            return executeLogin(request, response);
        }

        @Override
        protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            httpResponse.setContentType("application/json;charset=utf-8");
            try {
                //处理登录失败的异常
                Throwable throwable = e.getCause() == null ? e : e.getCause();
                R r = R.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());

                String json = new Gson().toJson(r);
                httpResponse.getWriter().print(json);
            } catch (IOException e1) {

            }
            return false;
        }

        /**
         * 获取请求的token
         */
        private String getRequestToken(HttpServletRequest httpRequest){
            //从header中获取token
            String token = httpRequest.getHeader("token");

            //如果header中不存在token,则从参数中获取token
            if(StringUtils.isBlank(token)){
                token = httpRequest.getParameter("token");
            }
            return token;
        }
    }

    如果成功获得token 则继续调用父类中executeLogin方法,此方法实现如下

    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
            AuthenticationToken token = createToken(request, response);
            if (token == null) {
                String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken " +
                        "must be created in order to execute a login attempt.";
                throw new IllegalStateException(msg);
            }
            try {
           // 创建主题,然后继续调用Realm中的登入认证方法doGetAuthenticationInfo Subject subject
    = getSubject(request, response); subject.login(token); return onLoginSuccess(token, subject, request, response); } catch (AuthenticationException e) { return onLoginFailure(token, e, request, response); } }

    调用子类中的createToken方法获得token对象,将token对象赋值给shiro subject 对象,从而在后面的认证方法中获得token

    3.将OAuth2Realm 注册到Shiro Seurity中,ShiroConfig.securityManager

     1 package io.renren.config;
     2 
     3 import io.renren.modules.sys.oauth2.OAuth2Filter;
     4 import io.renren.modules.sys.oauth2.OAuth2Realm;
     5 import org.apache.shiro.mgt.SecurityManager;
     6 import org.apache.shiro.session.mgt.SessionManager;
     7 import org.apache.shiro.spring.LifecycleBeanPostProcessor;
     8 import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
     9 import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    10 import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    11 import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    12 import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    13 import org.springframework.context.annotation.Bean;
    14 import org.springframework.context.annotation.Configuration;
    15 
    16 import javax.servlet.Filter;
    17 import java.util.HashMap;
    18 import java.util.LinkedHashMap;
    19 import java.util.Map;
    20 
    21 
    22 @Configuration
    23 public class ShiroConfig {
    24 
    25     @Bean("sessionManager")
    26     public SessionManager sessionManager(){
    27         DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    28         sessionManager.setSessionValidationSchedulerEnabled(true);
    29         sessionManager.setSessionIdCookieEnabled(false);
    30         System.out.println("获得sessionManager:" + sessionManager);
    31         return sessionManager;
    32     }
    33 
    34     @Bean("securityManager")
    35     public SecurityManager securityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {
    36         DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    37         securityManager.setRealm(oAuth2Realm);
    38         securityManager.setSessionManager(sessionManager);
    39         System.out.println("获得SecurityManager:" + securityManager);
    40         return securityManager;
    41     }
    42 
    43     @Bean("shiroFilter")
    44     public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
    45         ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
    46         shiroFilter.setSecurityManager(securityManager);
    47 
    48         //oauth过滤
    49         Map<String, Filter> filters = new HashMap<>();
    50         filters.put("oauth2", new OAuth2Filter());
    51         shiroFilter.setFilters(filters);
    52 
    53         Map<String, String> filterMap = new LinkedHashMap<>();
    54         filterMap.put("/webjars/**", "anon");
    55         filterMap.put("/druid/**", "anon");
    56         filterMap.put("/api/**", "anon");
    57         filterMap.put("/sys/login", "anon");
    58         filterMap.put("/**/*.css", "anon");
    59         filterMap.put("/**/*.js", "anon");
    60         filterMap.put("/**/*.html", "anon");
    61         filterMap.put("/fonts/**", "anon");
    62         filterMap.put("/plugins/**", "anon");
    63         filterMap.put("/swagger/**", "anon");
    64         filterMap.put("/favicon.ico", "anon");
    65         filterMap.put("/", "anon");
    66         filterMap.put("/**", "oauth2");
    67         shiroFilter.setFilterChainDefinitionMap(filterMap);
    68         System.out.println("获得shiroFilter:" + shiroFilter);
    69         return shiroFilter;
    70     }
    71 
    72     @Bean("lifecycleBeanPostProcessor")
    73     public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    74         return new LifecycleBeanPostProcessor();
    75     }
    76 
    77     @Bean
    78     public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    79         DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
    80         proxyCreator.setProxyTargetClass(true);
    81         return proxyCreator;
    82     }
    83 
    84     @Bean
    85     public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
    86         AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    87         advisor.setSecurityManager(securityManager);
    88         return advisor;
    89     }
    90 
    91 }
    View Code

    4.每次请求都会先调用OAuth2Realm中的doGetAuthenticationInfo方法验证token的合法性,然后再调用doGetAuthorizationInfo验证权限

    5.通过common.js判断当前客户端是否缓存了token,如果没有则跳转至login.html

    //登录token
    var token = localStorage.getItem("token");
    if(token == 'null'){
        parent.location.href = baseURL + 'login.html';
    }

    6.登入页面输入用户名、密码之后 缓存token,并跳转至index.html

    login: function () {
                var data = "username="+vm.username+"&password="+vm.password;
                $.ajax({
                    type: "POST",
                    url: baseURL + "sys/login",
                    data: data,
                    dataType: "json",
                    success: function(r){
                        if(r.code == 0){//登录成功
                            localStorage.setItem("token", r.token);
                            parent.location.href ='index.html';
                        }else{
                            vm.error = true;
                            vm.errorMsg = r.msg;
                        }
                    }
                });
            }
    View Code

    7.LoginController.java

    /**
         * 登录
         */
        @RequestMapping(value = "/sys/login", method = RequestMethod.POST)
        public Map<String, Object> login(String username, String password)throws IOException {
            //用户信息
            SysUserEntity user = sysUserService.queryByUserName(username);
    
            //账号不存在、密码错误
            if(user == null || !user.getPassword().equals(new Sha256Hash(password, user.getSalt()).toHex())) {
                return R.error("账号或密码不正确");
            }
    
            //账号锁定
            if(user.getStatus() == 0){
                return R.error("账号已被锁定,请联系管理员");
            }
    
            //生成token,并保存到数据库
            R r = sysUserTokenService.createToken(user.getUserId());
            return r;
        }

    8. 数据库token表结构,该表结构改成redis即可实现sso单点登入功能

      Field Type Comment
    user_id bigint(20) NOT NULL  
      token varchar(100) NOT NULL token
      expire_time datetime NULL 过期时间
      update_time datetime NULL 更新时间
  • 相关阅读:
    Linux下#!/usr/bin/env bash和#!/usr/bin/bash、#!/bin/bash的比较
    重要:1. hive查询时,先看表格元数据是怎样分区的,然后在where里写分区条件,否则会查询出错;2. 在Where条件中使用变量时,Hive查询会非常慢
    001机器人姿态RPY及移动路径方式
    linux 查看并对外开放端口(防火墙拦截处理)
    协程,twisted
    flask源码走读
    python 协程与go协程的区别
    Python 自带 RPC Demo
    这样逼格满满的弹出框消息提示你不心动吗?
    JS获取本周、本季度、本月、上月的开始日期、结束日期
  • 原文地址:https://www.cnblogs.com/rigid/p/7514708.html
Copyright © 2020-2023  润新知