Springboot,vue,shiro統合ログイン認証機能について


まずはセッション問題
従来のsession認証httpプロトコルは、ブラウザがサーバに要求を送信し、サーバがこの要求がどのユーザから送られたのか分からない無状態プロトコルである.要求がどのユーザから送られてきたかをサーバに知らせるためには,認証のためにユーザ名とパスワードをユーザに提供する必要がある.ブラウザが初めてサーバにアクセスすると(ログインインタフェースと仮定)、サーバがユーザー名とパスワードを検証した後、サーバは1つのセッションを生成し(最初に生成され、他は同じセッションを使用する)、そのセッションとユーザー情報を関連付けてから、セッションをブラウザに返し、ブラウザがセッションを受信してCookieに保存します.ユーザが2回目にサーバにアクセスするとCookie値が携帯され,サーバはCookie値を取得し,さらにsessionidを取得し,sessionidに基づいて関連するユーザ情報を取得する.
前後の分離開発により、従来のセッション方式ではログイン認証に使用できない.フロントエンドがngnixエージェントサーバに配備されているため、springbootがtomcatサーバに配備されているため、2つのプロジェクトにドメイン間の問題があるため、セッションは共有できない.セッションアドレスを取得するたびに、セッションオブジェクトは異なる
解決策1、初回ログイン時、バックエンドサーバがユーザーアカウントのパスワードが正しいと判断した後、sessionIdを生成し、期限切れを設定してredisサーバに保存し、フロントエンドに戻る
    @ApiOperation(value = "  ")
    @PostMapping("/login")
    public ResultVO loginPost(@RequestParam("userName")String userName, @RequestParam("userPass")String userPass){
        //         name passowrd  shrio UsernamePasswordToken
        userPass = new Md5Hash(userPass,userName,3).toString();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, userPass);
        // shiro     
        Subject subject = SecurityUtils.getSubject();

        //        ,    try catch
        try {
            //  login  ,  token
            subject.login(token);
            String sid = (String) subject.getSession().getId();
            //            ,     getPrincinpal()      
            User user= (User)subject.getPrincipal();
            //  sessionId
            String sessionId = (String) subject.getSession().getId();
            //    id   
            LoginVo loginVo=new LoginVo();
            loginVo.setUser(user);
            loginVo.setSessionId(sessionId);
            AppUser appUser=new AppUser();
            BeanUtils.copyProperties(user,appUser);
            return ResultVOUtil.success(loginVo);
        } catch (AuthenticationException e) {
            return ResultVOUtil.error(400,e.getMessage());
        }
    }

2、フロントエンドはバックエンドから戻ってきたセッションIDを受け取り、localStroage/sessionStroageに格納する
const user = {
    state: {
        id:window.sessionStorage.getItem('id'),
        userId:null,
        userName:null,
        userNickname:null,
        userIcon:null,
        userNumber:null,
        mail:null,
        roleId:null
    },
    mutations: {
        // id   sessionStorage ,id      
        SET_ID: (state, data) => {
            state.id = data
            window.sessionStorage.setItem('id', data)
        },
        //      
        SET_USER: (state, data) => {
            //         
            state.userId = data.userId
            state.userName = data.userName
            state.userNickname = data.userNickname
            state.userIcon = data.userIcon
            state.userNumber = data.userNumber
            state.mail = data.mail
            state.roleId = data.roleId
        },
        //  
        LOGOUT: (state) => {
            //         token
            state.id = null
            state.userId = null
            state.userName = null
            state.userNickname = null
            state.userIcon = null
            state.userNumber = null
            window.sessionStorage.removeItem('id')
        }
    },
    actions: {

    }
};

export default user;
 getUser(name,pass){
                login(name,pass).then(res => {
                    if(res.code==0){
                        this.$message({ message: "    ", type: 'success' })
                        this.$store.commit('SET_ID', res.data.sessionId)
                        this.$store.commit('SET_USER', res.data.user)
                        this.centerDialogVisible=false
                    }else{
                        this.$message({ message: res.msg, type: 'error' })
                    }
                })
            },

3、フロントエンドはルートごとに認証が必要なページにジャンプし、localStroage/sessionStroageのセッションIdの有無を判断し、なければログインページにジャンプする
私のはローカルルートで、みんなは異なる需要によって書きます
 beforeRouteEnter:(to,from,next)=>{
           next(vm => {
               vm.init()
               if (vm.$store.state.user.id==null){
                   vm.$message.error('      ,    ');
                   next('/home');
               }
           })

4、ブロックをカプセル化し、インタフェースを要求するたびに、要求ヘッダにsessionIdを携帯する
import axios from 'axios'
import { Notification } from 'element-ui';

export function request(config) {
  const instance = axios.create({
    baseURL: '/api',
    tiemout: 5000
  })

  instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

  instance.interceptors.request.use(config => {
    const id =window.sessionStorage.getItem('id')
    //  const id  =this.$store.state.user.id
    id && (config.headers.common['Authorization']  = 'beared'+id)
    return config
  }, err => {
    console.log(err);
  })

  instance.interceptors.response.use(res => {
    if (res.data.code == 401) {
      Notification({
        title: '    ',
        message: '      ,           ',
        position: 'bottom-right'
      })
    }
    return res.data
  }, err => {
    console.log(err);
  })

  return instance(config)
}

5、バックエンド統一ブロック判定要求ヘッダにセッションIdがあるか、セッションIdがないか、またはセッションIdが期限切れであるか、401を返す
6、フロントエンドは401ステータスコードを得て、ログインページにリダイレクトする
登録認証のまとめに用いられる技術は,ルーティングガード,axioxパッケージ要求応答ブロック,セキュリティフレームワークshiro,vuex,持続化プラグイン(VuexPersistence)のキーは,セッションが従来のセッションをshiro管理に渡し,セッション検証メカニズムを書き換え,リクエストヘッダ(セッションId)に基づいてセッション情報を取得するように設定することである.
public class CustomSessionManager extends DefaultWebSessionManager {
    /**
     *       
     */
    public CustomSessionManager() {
        super();
        setGlobalSessionTimeout(DEFAULT_GLOBAL_SESSION_TIMEOUT * 48);
    }

    /**
     *    Authorization:sessionid
     *   sessionid     
     */
    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //       
        String id= WebUtils.toHttp(request).getHeader("Authorization");
        if (StringUtils.isEmpty(id)) {
            //       sessionid
            return super.getSessionId(request,response);
        }else {
            id=id.replaceAll("beared","");
            //     
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, "header");
            //id   
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            //     
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        }
    }

}


shiro構成redisストレージセッションを構成する利点は、セッションIdに基づいてセッションをデータベースに保存することで、サーバを再起動してもセッションを失うことはありません.
フロントエンド実装:vuex永続化プラグインVuexPersistenceをインストールし、ユーザー情報とsessionIdをvuexに保存
パッケージングブロッキングは,インタフェースを要求するたびにvuexが取り出したセッションIdをリクエストヘッダに持ち込み,バックエンドは経路に基づいて権限があるか認証されているか否かを判断する.
マイシロ構成
@Configuration
public class ShiroConfig {


	/**
	 *   ShiroFilterFactoryBean
	 */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//       
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		
		//  Shiro     
		/**
		 * Shiro     ,            
		 *          :
		 *       anon:     (  )    
		 *       authc:          
		 *       user:     rememberMe         
		 *       perms:                 
		 *       roles:                 
		 */
        Map filters = shiroFilterFactoryBean.getFilters();
        filters.put("roles",shiroRoleFilter());
        filters.put("authc",shiroLoginFilter());

        Map filterMap = new LinkedHashMap();

       filterMap.put("/byuser/**","authc");
//        filterMap.put("/byuser/home","anon");

		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        shiroFilterFactoryBean.setFilters(filters);

		return shiroFilterFactoryBean;
	}

    @Bean(name="shiroLoginFilter")
    public ShiroLoginFilter shiroLoginFilter() {
        return new ShiroLoginFilter();
    }
    @Bean(name="shiroRoleFilter")
    public ShiroRoleFilter shiroRoleFilter() {
        return new ShiroRoleFilter();
    }

    @Bean(name="corsBasicFilter")
    public CorsBasicFilter corsBasicFilter() {
        return new CorsBasicFilter();
    }

	/**
	 *   DefaultWebSecurityManager
	 */
	@Bean(name="securityManager")
	public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("loginRealm")LoginRealm userRealm){
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//  realm
		securityManager.setRealm(userRealm);
        //                   
        securityManager.setSessionManager(sessionManager());
        //     redis              
//        securityManager.setCacheManager(cacheManager());
		return securityManager;
	}
	
	/**
	 *   Realm
	 */
	@Bean(name="loginRealm")
	public LoginRealm getRealm(){
		return new LoginRealm();
	}



    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;

    /**
     * 1.redis    ,  redis
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(7000);
        return redisManager;
    }

    /**
     * 2.sessionDao
     */
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO sessionDAO = new RedisSessionDAO();
        sessionDAO.setRedisManager(redisManager());
        return sessionDAO;
    }

    /**
     * 3.     
     */
    public DefaultWebSessionManager sessionManager() {
        //     
        CustomSessionManager sessionManager = new CustomSessionManager();
       // sessionManager.setSessionDAO(redisSessionDAO());
        //     Cookie        session      
//        sessionManager.setSessionIdCookieEnabled(false);
//        sessionManager.setSessionIdUrlRewritingEnabled(false);
        return sessionManager;
    }

    /**
     * 4.     
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        return redisCacheManager;
    }




    //   shior     
//    @Bean
//    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
//        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
//        advisor.setSecurityManager(securityManager);
//        return advisor;
//    }

//	@Bean
//	public ShiroDialect getShiroDialect(){
//		return new ShiroDialect();
//	}
}