Springboot,vue,shiro統合ログイン認証機能について
まずはセッション問題
従来のsession認証httpプロトコルは、ブラウザがサーバに要求を送信し、サーバがこの要求がどのユーザから送られたのか分からない無状態プロトコルである.要求がどのユーザから送られてきたかをサーバに知らせるためには,認証のためにユーザ名とパスワードをユーザに提供する必要がある.ブラウザが初めてサーバにアクセスすると(ログインインタフェースと仮定)、サーバがユーザー名とパスワードを検証した後、サーバは1つのセッションを生成し(最初に生成され、他は同じセッションを使用する)、そのセッションとユーザー情報を関連付けてから、セッションをブラウザに返し、ブラウザがセッションを受信してCookieに保存します.ユーザが2回目にサーバにアクセスするとCookie値が携帯され,サーバはCookie値を取得し,さらにsessionidを取得し,sessionidに基づいて関連するユーザ情報を取得する.
前後の分離開発により、従来のセッション方式ではログイン認証に使用できない.フロントエンドがngnixエージェントサーバに配備されているため、springbootがtomcatサーバに配備されているため、2つのプロジェクトにドメイン間の問題があるため、セッションは共有できない.セッションアドレスを取得するたびに、セッションオブジェクトは異なる
解決策1、初回ログイン時、バックエンドサーバがユーザーアカウントのパスワードが正しいと判断した後、sessionIdを生成し、期限切れを設定してredisサーバに保存し、フロントエンドに戻る
2、フロントエンドはバックエンドから戻ってきたセッションIDを受け取り、localStroage/sessionStroageに格納する
3、フロントエンドはルートごとに認証が必要なページにジャンプし、localStroage/sessionStroageのセッションIdの有無を判断し、なければログインページにジャンプする
私のはローカルルートで、みんなは異なる需要によって書きます
4、ブロックをカプセル化し、インタフェースを要求するたびに、要求ヘッダにsessionIdを携帯する
5、バックエンド統一ブロック判定要求ヘッダにセッションIdがあるか、セッションIdがないか、またはセッションIdが期限切れであるか、401を返す
6、フロントエンドは401ステータスコードを得て、ログインページにリダイレクトする
登録認証のまとめに用いられる技術は,ルーティングガード,axioxパッケージ要求応答ブロック,セキュリティフレームワークshiro,vuex,持続化プラグイン(VuexPersistence)のキーは,セッションが従来のセッションをshiro管理に渡し,セッション検証メカニズムを書き換え,リクエストヘッダ(セッションId)に基づいてセッション情報を取得するように設定することである.
shiro構成redisストレージセッションを構成する利点は、セッションIdに基づいてセッションをデータベースに保存することで、サーバを再起動してもセッションを失うことはありません.
フロントエンド実装:vuex永続化プラグインVuexPersistenceをインストールし、ユーザー情報とsessionIdをvuexに保存
パッケージングブロッキングは,インタフェースを要求するたびにvuexが取り出したセッションIdをリクエストヘッダに持ち込み,バックエンドは経路に基づいて権限があるか認証されているか否かを判断する.
マイシロ構成
従来の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();
// }
}