最初の質問セッションです
伝統的なセッション認証
、HTTPプロトコルはつまり、ブラウザがサーバに要求を送信し、サーバは要求がどのユーザーに送信された知らない、ステートレスなプロトコルです。サーバがユーザに送信され、認証用のユーザー名とパスワードを提供するために、ユーザーが必要とされている要求を知らせるために。ブラウザが最初(インタフェースがログインであると仮定して)サーバーにアクセスし、サーバー認証のユーザー名とパスワード、サーバがセッションIDを生成します(初回のみが生成されます、他は同じセッションIDを使用します)、およびセッションとユーザ情報アソシエイト、ブラウザに返さSESSIONID、その後、ブラウザはユーザーがサーバーが二度目値が値を取得するためにクッキー、クッキーサーバを運び、その後、セッションIDを取得しますでアクセスする際に、クッキーを保存するためにセッションID受け、セッションIDを取得関連付けることに従って、ユーザー情報。
前部の分離および開発の後ので、伝統的な方法は、セッションのログイン認証に使用することができず、ngnixに配備フロントエンドプロキシサーバがあるため、セッションを共有することができないので、サーバのTomcat springbootは、それぞれ、二つのプロジェクトは、クロスドメインの問題があり、展開されています時間が異なるセッションのアドレスを取得し、セッションオブジェクトが異なっています
ソリューション
1の後に、ユーザーアカウントのパスワードを決定するために、バックエンドサーバーで最初のログが正しい、セッションIDを生成し、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に示すように、リターンの後端部の前端はlocalStroage / sessionStroageレーンに格納され得るセッションID
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、ページに各ルートジャンプのフロントエンドが認証を必要とする、セッションIDは、ログインページにジャンプしていないかどうかをlocalStroage / sessionStroageを決定
私は、ローカルルーティングを持って、我々は様々なニーズに応じて書きます
beforeRouteEnter:(to,from,next)=>{
next(vm => {
vm.init()
if (vm.$store.state.user.id==null){
vm.$message.error('你还没有登录,请先登录');
next('/home');
}
})
図4に示すように、パッケージインターセプタ、各リクエストインタフェース、セッションIDを運ぶために事前に要求
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が有効期限切れか統一切片は、401を返します
図6に示すように、ステータスコード401を得るためのフロントエンドは、ログインページにリダイレクトされます
ログイン認証がまとめ
要求インターセプタ、セキュリティフレームワーク四郎、vuex及び持続プラグイン(VuexPersistence)に応じてガード、axioxパッケージのルーティングに使用される技術
のキーポイントは、セッションのセッションである
伝統的なセッション管理を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;
}
}
}
設定史郎は、ストレージのコンフィギュレーションセッションをRedisのような利点が、データベースに保存されたセッションIDのセッションに応じて、サーバー・セッションを再起動しても失われないこと
フロントエンドの実装:取付vuex永続プラグVuexPersistence、及びvuexにセッションID内に格納されたユーザ情報
パッケージインターセプタ、各要求インターフェースは、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<String, Filter> filters = shiroFilterFactoryBean.getFilters();
filters.put("roles",shiroRoleFilter());
filters.put("authc",shiroLoginFilter());
Map<String,String> filterMap = new LinkedHashMap<String,String>();
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();
// }
}