项目日记day0225
一、登录视图跳转的改进
方法一:使用响应拦截器
我们可以这样做:
在前端加一个响应拦截器,后端资源给的所有响应都会经过响应拦截器,可以只在响应拦截器中做一下"unlogin"字符串的判断,通过响应拦截器来做统一的视图跳转。
响应拦截器是axios的一大特色之一。
在前端项目中的main.js加入以下代码:
// 添加响应拦截器
Vue.prototype.$axios.interceptors.response.use(response => {
let result = response.data;
if(result.loginSign == false){
Vue.prototype.$swal.fire({
icon: 'error',
title: "用户未登录,请登陆后访问!",
showConfirmButton: false,
timer: 1500,
onClose:()=>{
router.push("/");//在main.js中可以直接使用router
}
})
}
return response;
});
进行测试,未登录的情况下访问"http://localhost:8080/#/main/main"点击测试按钮:
页面进行未登录提示,并跳转到登录页。
现在我们进行这么一个测试:输入后端中的一个控制器地址:http://localhost/jiazhong-office/login/unLogin,发现给我们响应的是json数组,而不是直接跳转到登陆页面。这是因为响应拦截器拦截的是从前端发出来请求后给予的响应,此时,我们只是直接在地址栏上输入了请求的地址,所以不会跳转到登录页面。
方法二:使用axios的请求拦截器与取消请求
我们可以这样做:
在前端设立一个请求拦截器,前端所有的请求都会经过请求拦截器,当请求拦截器检测到用户未登录时,会直接将视图跳转到登录视图上,检测到用户已登陆,将请求发送到后端。
这里有两种实现方式:
1.将用户登陆状态存入sessionStorage中(推荐使用)
- 登陆成功后,像浏览器中存入"sessionStorage=true"的键值对
- 添加请求拦截器(虽然拦截了请求,但只是做了一个表面上的样子,实际上请求还是发出去了)
- 用户未登录,取消请求
- 将SpringScurity配置的loginPage改为"http://localhost:8080"
- 进行未登录测试,访问前端"http://localhost:8080/#/main/mian",点击测试按钮,提示后跳转到登录页;访问后端"localhost/jiazhong-office/test",直接跳转到前端登录页。测试成功!
2.使用全局变量表示用户登陆状态
- 在main.js中设置全局变量token为false(注意:这里不能以Vue.prototype.$token=false来表示,因为这样是注册原型属性,原型属性只能被读,不能被修改)
- 登陆成功后,将token属性修改为true
-
在main.js中的请求拦截器中判断,如果token的属性为true则取消请求
-
测试成功!
二、权限角色在数据库中获取
我们之前对于用户角色是以将其写为固定的角色来进行处理的,但是在实际的业务中,角色是在数据库中获取的。
1.建立角色实体类并在在账户实体类中加入一个角色的List集合
package com.jiazhong.office.model;
/**
* @ClassName: Role
* @Description: TODO
* @Author: JiaShiXi
* @Date: 2021/2/25 13:23
* @Version: 1.0
**/
public class Role {
private Integer role_id;
private String role_name;
private String role_desc;
public Integer getRole_id() {
return role_id;
}
public void setRole_id(Integer role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_desc() {
return role_desc;
}
public void setRole_desc(String role_desc) {
this.role_desc = role_desc;
}
}
2.利用关联映射,映射Account中的roles属性的值
AccountDao:
package com.jiazhong.office.dao.rbac;
import com.jiazhong.office.model.Account;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
/**
* @InterfaceName: AccountDao
* @Description: TODO 账户持久层
* @Author: JiaShiXi
* @Date: 2021/2/23 20:29
* @Version: 1.0
**/
@Repository
public interface AccountDao {
/**
* 通过账户名查询账户信息
* @param account_name 账户名
* @return 账户对象
*/
@Select("select * from tbl_account where account_name = #{account_name}")
@Results(
@Result(
property = "roles", //映射的属性
column = "account_id", //映射的字段
javaType = java.util.List.class, //映射属性的java类型
many = @Many(select = "com.jiazhong.office.dao.rbac.RoleDao.getRoleByAccountId") //多方使用的sql语句
)
)
public Account queryAccountByAccountName(@Param("account_name") String account_name);
}
RoleDao:
package com.jiazhong.office.dao.rbac;
import com.jiazhong.office.model.Role;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
/**
* @InterfaceName: RoleDao
* @Description: TODO
* @Author: JiaShiXi
* @Date: 2021/2/25 13:31
* @Version: 1.0
**/
public interface RoleDao {
/**
* 通过账户ID查询用户所拥有的角色
* @param account_id
* @return
*/
@Select("select * from tbl_role where role_id in(select role_id from tbl_account_role where account_id=#{account_id})")
public Role getRoleByAccountId(@Param("account_id") Integer account_id);
}
3.修改userDetailServiceImpl类
package com.jiazhong.office.service.rbac;
import com.jiazhong.office.dao.rbac.AccountDao;
import com.jiazhong.office.model.Account;
import com.jiazhong.office.model.Role;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName: UserDetailServiceImpl
* @Description: TODO
* @Author: JiaShiXi
* @Date: 2021/2/23 20:26
* @Version: 1.0
**/
@Service("userDetailsService")
@Transactional
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private AccountDao accountDao;
/**
* 根据用户名获得用户信息
*
* @param username 用户名
* @return 用户信息
* @throws UsernameNotFoundException 用户名不存在异常
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Account account = accountDao.queryAccountByAccountName(username);
if (account == null){
throw new UsernameNotFoundException("账户名不存在");
}
//获取用户角色信息
List<GrantedAuthority> grantedAuthorities = getGrantedAuthorities(account.getRoles());
/**
* 创建UserDetails实现类对象User,将认证信息传给User对象进行认证
* 参数1:要认证用户的用户名
* 参数2:要认证用户的密码
* 参数3:要认证用户所拥有的权限集合
*/
//User userAuth = new User(account.getAccount_name(),account.getAccount_password(),grantedAuthorities);
/**
* 创建UserDetails实现类对象User,将认证信息传给User对象进行认证
* 参数1:要认证用户的用户名
* 参数2:要认证用户的密码
* 参数3:账户是否可以
* 参数4:账户是否已经过期
* 参数5:凭证是否已经过期
* 参数6:账户是否已经被锁定
* 参数7:要认证用户所拥有的权限集合
*/
User userAuth = new User(
account.getAccount_name(),
account.getAccount_password(),
account.getAccount_status()==0?true:account.getAccount_status()==-1?false:true,
true,true,
account.getAccount_status()==0?true:account.getAccount_status()==1?false:true,
grantedAuthorities
);
return userAuth;
}
/**
* 通过传入的角色获取存储用户角色权限集合
* @param roles 角色集合
* @return
*/
public List<GrantedAuthority> getGrantedAuthorities(List<Role> roles){
//创建一个用于存储用户认证权限的集合
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
//遍历角色
for (Role role : roles) {
grantedAuthorities.add(new SimpleGrantedAuthority(role.getRole_name()));
}
return grantedAuthorities;
}
}
登陆成功后,SpringScurity会将User认证对象中的信息存入到session中,我们可以通过获取一些对我们有用的信息进行处理。
我们在登陆成功的处理器中,获取一下登陆成功后session中所有的key,发现是SPRING_SECURITY_CONTEXT
。
我们再看一下这个key对应的值是什么?
org.springframework.security.core.context.SecurityContextImpl@f684df11: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@f684df11: Principal: org.springframework.security.core.userdetails.User@586034f: Username: admin; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_市场专员,ROLE_教学总监,ROLE_校长,ROLE_讲师; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@166c8: RemoteIpAddress: 0:0:0:0:0:0:0:1; SessionId: 1F031DB88520531968829B142CF7A4D5; Granted Authorities: ROLE_市场专员, ROLE_教学总监, ROLE_校长, ROLE_讲师
我们发现存有一个User对象,其中有认证通过的用户信息。
三、登陆成功后,将需要的当前账户信息存入到session中
1.新建当前用户类,存储之后生成权限菜单所需要的信息
package com.jiazhong.office.commons;
import java.util.List;
/**
* @ClassName: CurAccount 当前用户类
* @Description: TODO 存储当前登录用户的信息
* @Author: JiaShiXi
* @Date: 2021/2/25 14:05
* @Version: 1.0
**/
public class CurAccount {
private String account_name;
private List<String> roleNames;
public String getAccount_name() {
return account_name;
}
public void setAccount_name(String account_name) {
this.account_name = account_name;
}
public List<String> getRoleNames() {
return roleNames;
}
public void setRoleNames(List<String> roleNames) {
this.roleNames = roleNames;
}
@Override
public String toString() {
return "CurAccount{" +
"account_name='" + account_name + '\'' +
", roleNames=" + roleNames +
'}';
}
}
2.新建常量类,存储项目中所用到的常量
package com.jiazhong.office.commons;
/**
* @ClassName: Constant 常量类
* @Description: TODO
* @Author: JiaShiXi
* @Date: 2021/2/25 14:08
* @Version: 1.0
**/
public class Constant {
/**
* session中存储的当前登陆的用户
*/
public static final String SESSION_CUR_ACCOUNT = "SESSION_CUR_ACCOUNT";
}
3.在登陆成功的处理器中,获得session中的认证信息,封装到CurAccount中,并将存入CurAccount对象到session中
/**
* 登陆成功后的操作
* @return
*/
@GetMapping("/result/loginSuccess")
public Result loginSuccess(HttpSession session) {
/**
* 获得认证信息,封装到CurAccount中,并存入到session中
*/
//获取当前的认证用户
User user = (User) session.getAttribute("SPRING_SECURITY_CONTEXT");
//将当前登录者的信息封装到当前登录者类中
CurAccount curAccount = new CurAccount();
curAccount.setAccount_name(user.getUsername());
List<String> roleNames = new ArrayList<>();
for (GrantedAuthority authority : user.getAuthorities()) {
roleNames.add(authority.getAuthority());
}
curAccount.setRoleNames(roleNames);
//将当前用户存入session中
session.setAttribute(Constant.SESSION_CUR_ACCOUNT,curAccount);
return Result.success("登陆成功");
}
四、权限菜单的静态实现
<router-view />
的作用是显示当前视图的根路由,加上该路由视图标签后,路由才会生效。
实现方法:
1.编写welcome.vue欢迎视图,并将其设置为main视图的根路由,访问main视图时,直接显示welcome.vue视图。
welcome.vue
<template>
<div class="container-welcome">
<h1>欢迎进入加中实训-云办公平台</h1>
</div>
</template>
<style scoped>
.container-welcome{
display: flex;
justify-content: center;
}
</style>
index.js中配置路由
//配置main路由
path:"/main/main",
name:"Main",
component:Main,
/**配置main路由的子路由 */
children:[
{
path:"/",//配置子路由中的根路由
name:"Welcome",
component:Welcome
}
]
2.编写角色管理视图(role.vue)、用户管理视图(user.vue),并进行路由配置
<template>
<div class="container-role">
<h1>角色管理</h1>
</div>
</template>
<style scoped>
.container-welcome{
display: flex;
justify-content: center;
}
</style>
<template>
<div class="container-user">
<h1>用户管理</h1>
</div>
</template>
<style scoped>
.container-welcome{
display: flex;
justify-content: center;
}
</style>
3.修改main视图,当单击"用户管理"、“角色管理"时,分别跳转到"用户视图"和"角色视图”。
这里需要用到element ui 中菜单项的一个事件:
4.效果:
刚进入:
点击用户管理菜单项:
单击角色管理菜单项: