后端:GitHub - chenqi13814529300/my-csdn-shiro
前端:GitHub - chenqi13814529300/my-shiro-ui
sql代码在后端代码里,这里没有用jpa,因为易上手,难精通,比较抽象,不利于未来发展
目录
数据库设计
后端项目构建
创建项目
pom依赖
application
实现效果
具体实现
整体目录
entity类
dto层,用于封装前端传来的用户名和密码
Mapper.xml层跟数据库相关
dao层,也就是mapper接口层
service层,业务处理层
contorller层
config
auth
utils
hander
启动类
前端项目构建
创建项目
前端具体实现
目录如下
main.js router.js token.js
api
views
流程思路
学到这个阶段,大家Mysql应该不陌生了,既然是走Mybatis的路线,先搭建一个健壮的数据结构很重要,秉着高内聚低耦合去设计。
不可取的设计方式如下:
Id | username | password | role | permission |
我建立如下数据结构,一组是用户角色权限,一组是角色的token
用户角色权限的一组表
user
user_role
role
role_permission
permission
角色token存储的表
user_token (这些数据是生成的,你们设计个表即可)
按步骤来即可
org.apache.shiro
shiro-spring
1.3.2
org.apache.shiro
shiro-ehcache
1.4.0
org.springframework.boot
spring-boot-starter-freemarker
org.springframework.boot
spring-boot-starter-aop
org.springframework.boot
spring-boot-starter-web
com.baomidou
mybatis-plus-boot-starter
3.4.2
mysql
mysql-connector-java
runtime
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
commons-lang
commons-lang
2.6
server:
port: 9000
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/myshiro?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
username: root
password: 123
name: defaultDataSource
建立目录
auth包下放权限过滤器,判断token等
cofig包下是shiro的配置文件
dto包下是前端传入后端的用户名和密码的实体类
handler包下是全局异常处理类
其他的你们应该知道了
不同用户登录进去有不同的权限
登录后如下
User
package com.mycsdnshiro.mycsdnshiro.entity;
public class User {
private Integer userId;
private String username;
private String password;
private Integer roleId;
private String roleName;
private String permission;
private Integer permissionId;
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
", roleId=" + roleId +
", roleName='" + roleName + '\'' +
", permission='" + permission + '\'' +
", permissionId=" + permissionId +
'}';
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public Integer getPermissionId() {
return permissionId;
}
public void setPermissionId(Integer permissionId) {
this.permissionId = permissionId;
}
public User() {
}
public User(Integer userId, String username, String password, Integer roleId, String roleName, String permission, Integer permissionId) {
this.userId = userId;
this.username = username;
this.password = password;
this.roleId = roleId;
this.roleName = roleName;
this.permission = permission;
this.permissionId = permissionId;
}
}
UserToken
package com.mycsdnshiro.mycsdnshiro.entity;
import java.time.LocalDateTime;
public class UserToken {
private Integer userId;
private String token;
// 过期时间
private LocalDateTime expireTime;
// 更新时间
private LocalDateTime updateTime;
@Override
public String toString() {
return "UserTokenMapper{" +
"userId=" + userId +
", token='" + token + '\'' +
", expireTime=" + expireTime +
", updateTime=" + updateTime +
'}';
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public LocalDateTime getExpireTime() {
return expireTime;
}
public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
public UserToken(Integer userId, String token, LocalDateTime expireTime, LocalDateTime updateTime) {
this.userId = userId;
this.token = token;
this.expireTime = expireTime;
this.updateTime = updateTime;
}
public UserToken() {
}
}
RoleAndPermission
package com.mycsdnshiro.mycsdnshiro.entity;
public class RoleAndPermission {
private Integer userId;
private Integer roleId;
private String roleName;
private String permission;
public RoleAndPermission(Integer userId, Integer roleId, String roleName, String permission) {
this.userId = userId;
this.roleId = roleId;
this.roleName = roleName;
this.permission = permission;
}
@Override
public String toString() {
return "RoleAndPermission{" +
"userId=" + userId +
", roleId=" + roleId +
", roleName='" + roleName + '\'' +
", permission='" + permission + '\'' +
'}';
}
public RoleAndPermission() {
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public Integer getRoleId() {
return roleId;
}
public void setRoleId(Integer roleId) {
this.roleId = roleId;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
package com.mycsdnshiro.mycsdnshiro.dto;
public class LoginDTO {
private String username;
private String password;
public LoginDTO() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public LoginDTO(String username, String password) {
this.username = username;
this.password = password;
}
@Override
public String toString() {
return "LoginDTO{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
UserMapper
这些代码都没什么必要说的,来看此文章你们应该会增删改查了吧
UserTokenMapper
insert into user_token
(user_id,token,update_time,expire_time)
values(#{userId},#{token},#{updateTime},#{expireTime})
UPDATE user_token
set expire_time=#{expireTime},
token=#{token},
update_time=#{updateTime}
where user_id=#{userId};
UserMapper
package com.mycsdnshiro.mycsdnshiro.mapper;
import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface UserMapper {
User findByUsername(String username);
User findByUserId(Integer userId);
List rolePermissionByRoleId(Integer roleId);
}
UserTokenMapper
package com.mycsdnshiro.mycsdnshiro.mapper;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
public interface UserTokenMapper {
UserToken findByToken(String token);
UserToken findByUserId(Integer userId);
int insertUserToken(UserToken userToken);
int updateUserToken(UserToken userToken);
}
你们将明白为什么要多这一层,而不直接controller调用mapper接口
ShiroLoginService
package com.mycsdnshiro.mycsdnshiro.service;
import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
import java.util.List;
import java.util.Map;
public interface ShiroLoginService {
List queryAllUser();
User findByUsername(String username);
Map createToken(Integer userId);
void logout(String token);
UserToken findByToken(String accessToken);
User findByUserId(Integer userId);
List rolePermissionByRoleId(Integer roleId);
}
ShiroLoginServiceImpl
package com.mycsdnshiro.mycsdnshiro.service.impl;
import com.mycsdnshiro.mycsdnshiro.auth.TokenGenerator;
import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
import com.mycsdnshiro.mycsdnshiro.mapper.UserMapper;
import com.mycsdnshiro.mycsdnshiro.mapper.UserTokenMapper;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Service
public class ShiroLoginServiceImpl implements ShiroLoginService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserTokenMapper userTokenMapper;
private final static int EXPIRE=12;
@Override
public List queryAllUser() {
return null;
}
@Override
public User findByUsername(String username) {
return userMapper.findByUsername(username);
}
// 给当前登录的用户生成token
@Override
public Map createToken(Integer userId) {
Map result=new HashMap<>();
// 生成一个token,代表这个用户的标识
String token = TokenGenerator.generateValue();
// 当前时间
LocalDateTime now = LocalDateTime.now();
// 过期时间
LocalDateTime expireTime = now.plusHours(EXPIRE);
// 通过Id 判断是否生成过token 若有则更新,若没有则添加
UserToken tokenEntity = userTokenMapper.findByUserId(userId);
if(tokenEntity==null){
// 新增
tokenEntity = new UserToken();
tokenEntity.setUserId(userId);
tokenEntity.setToken(token);
tokenEntity.setUpdateTime(now);
tokenEntity.setExpireTime(expireTime);
// 新增就sql添加
userTokenMapper.insertUserToken(tokenEntity);
}else {
// 更新
tokenEntity.setToken(token);
tokenEntity.setUpdateTime(now);
tokenEntity.setExpireTime(expireTime);
// 更新就sql更新
userTokenMapper.updateUserToken(tokenEntity);
}
result.put("token",token);
result.put("expire",expireTime);
return result;
}
@Override
public void logout(String token) {
}
@Override
public UserToken findByToken(String accessToken) {
return userTokenMapper.findByToken(accessToken);
}
@Override
public User findByUserId(Integer userId) {
return userMapper.findByUserId(userId);
}
@Override
public List rolePermissionByRoleId(Integer roleId) {
return userMapper.rolePermissionByRoleId(roleId);
}
}
ShiroLoginController
用于用户登录的
package com.mycsdnshiro.mycsdnshiro.controller;
import com.mycsdnshiro.mycsdnshiro.dto.LoginDTO;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/user")
public class ShiroLoginController {
@Autowired
private ShiroLoginService shiroLoginService;
@PostMapping("login")
public Map login(@RequestBody @Validated LoginDTO loginDTO, BindingResult bindingResult) {
// 把后端处理后的数据,封装在里面,返回给前端
Map result = new HashMap<>();
//前端返回值校验
if (bindingResult.hasErrors()) {
result.put("status", 400);
result.put("msg", bindingResult.getFieldError().getDefaultMessage());
return result;
}
// 获取登录前端传入的值
String username = loginDTO.getUsername();
String password = loginDTO.getPassword();
System.out.println(username);
// 在数据库中查找是否有此用户名
User user = shiroLoginService.findByUsername(username);
if(user==null){
result.put("status",300);
result.put("mg","账号不存在");
}else if(!user.getPassword().equals(password)){
result.put("status",400);
result.put("mg","密码错误");
}else {
// 账号和密码验证正确,生成新token或者更新token并保存到数据库中
result = shiroLoginService.createToken(user.getUserId());
result.put("status",200);
result.put("mg","检验通过,登录成功");
}
return result;
}
}
ShiroBaseController
用于用户个人权限和对应操作权限的判断
package com.mycsdnshiro.mycsdnshiro.controller;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("base")
public class ShiroBaseController {
@RequiresRoles({"admin"}) //没有的话 AuthorizationException
@GetMapping("/admin")
public Map admin(@RequestHeader("token")String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有admin角色");
return map;
}
@RequiresRoles({"teacher"}) //没有的话 AuthorizationException
@GetMapping("/teacher")
public Map teacher(@RequestHeader("token")String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有teacher角色");
return map;
}
@RequiresRoles({"student"}) //没有的话 AuthorizationException
@GetMapping("/student")
public Map p(@RequestHeader("token")String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有student角色");
return map;
}
@RequiresPermissions({"insert"}) //没有的话 AuthorizationException
@PostMapping("insert")
public Map save(@RequestHeader("token") String token) {
System.out.println("insert");
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有insert的权力");
return map;
}
@RequiresPermissions({"delete"}) //没有的话 AuthorizationException
@DeleteMapping("delete")
public Map delete(@RequestHeader("token") String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有delete的权力");
return map;
}
@RequiresPermissions({"update"}) //没有的话 AuthorizationException
@PutMapping("update")
public Map update(@RequestHeader("token") String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有update的权力");
return map;
}
@RequiresPermissions({"select"}) //没有的话 AuthorizationException
@GetMapping("select")
public Map select(@RequestHeader("token") String token) {
Map map = new HashMap();
map.put("status", 200);
map.put("msg", "当前用户有select的权力");
return map;
}
}
ShiroConfig配置文件
package com.mycsdnshiro.mycsdnshiro.config;
import com.mycsdnshiro.mycsdnshiro.auth.AuthFilter;
import com.mycsdnshiro.mycsdnshiro.auth.AuthRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean("securityManager")
public SecurityManager securityManager(AuthRealm authRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(authRealm);
securityManager.setRememberMeManager(null);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shifoFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
//auth过滤
Map filters = new HashMap<>();
filters.put("auth", new AuthFilter());
shiroFilter.setFilters(filters);
LinkedHashMap filterMap = new LinkedHashMap<>();
// auno 匿名访问 auth验证
filterMap.put("/webjars/**", "anon");
filterMap.put("/druid/**", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/swagger/**", "anon");
filterMap.put("/v2/api-docs", "anon");
filterMap.put("/swagger-ui.html", "anon");
filterMap.put("/swagger-resources/**", "anon");
filterMap.put("/doc.html", "anon");
// 下面这个是放行这个接口(不进行权限判断),因为我们登录需要请求这个接口
filterMap.put("/user/login", "anon");
//除了以上,其他都需要权限验证
filterMap.put("/**", "auth");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
用户权限判断的一些类
AuthFilter
package com.mycsdnshiro.mycsdnshiro.auth;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.mycsdnshiro.mycsdnshiro.utils.HttpContextUtil;
import com.mycsdnshiro.mycsdnshiro.utils.TokenUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Component
public class AuthFilter extends AuthenticatingFilter {
// 定义jackson对象
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 生成自定义token
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token
String token = TokenUtil.getRequestToken((HttpServletRequest) request);
System.out.println("后端获取前端headers或者参数处的token="+token);
return new AuthToken(token);
}
/**
* 步骤1.所有请求全部拒绝访问
*
* @param request
* @param response
* @param mappedValue
* @return
*/
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
if (((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())) {
return true;
}
return false;
}
/**
* 步骤2,拒绝访问的请求,会调用onAccessDenied方法,onAccessDenied方法先获取 token,再调用executeLogin方法
*
* @param request
* @param response
* @return
* @throws Exception
*/
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//获取请求token,如果token不存在,直接返回
String token = TokenUtil.getRequestToken((HttpServletRequest) request);
System.out.println("前端请求token="+token);
if (StringUtils.isBlank(token)) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
httpResponse.setCharacterEncoding("UTF-8");
Map result = new HashMap<>();
result.put("status", 403);
result.put("msg", "请先登录");
String json = MAPPER.writeValueAsString(result);
httpResponse.getWriter().print(json);
return false;
}
return executeLogin(request, response);
}
/**
* token失效时候调用
*/
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setContentType("application/json;charset=utf-8");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
httpResponse.setCharacterEncoding("UTF-8");
try {
//处理登录失败的异常
Throwable throwable = e.getCause() == null ? e : e.getCause();
Map result = new HashMap<>();
result.put("status", 403);
result.put("msg", "登录凭证已失效,请重新登录");
String json = MAPPER.writeValueAsString(result);
httpResponse.getWriter().print(json);
} catch (IOException e1) {
}
return false;
}
}
AuthToken
package com.mycsdnshiro.mycsdnshiro.auth;
import org.apache.shiro.authc.UsernamePasswordToken;
public class AuthToken extends UsernamePasswordToken {
private String token;
public AuthToken(String token) {
this.token = token;
}
@Override
public Object getPrincipal() {
return token;
}
@Override
public Object getCredentials() {
return token;
}
}
TokenGenerator
生成Token,其中搭配utils工具包使用,在下文
package com.mycsdnshiro.mycsdnshiro.auth;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class TokenGenerator {
// token生成器
public static String generateValue() {
return generateValue(UUID.randomUUID().toString());
}
private static final char[] hexCode = "0123456789abcdefgh".toCharArray();
public static String toHexString(byte[] data){
if (data==null){
return null;
}
StringBuffer r = new StringBuffer(data.length * 2);
for (byte b: data){
r.append(hexCode[(b >> 4) & 0xF]);
r.append(hexCode[(b & 0xF)]);
}
return r.toString();
}
// 生成Token
public static String generateValue(String param){
try {
MessageDigest algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(param.getBytes());
byte[] digest = algorithm.digest();
return toHexString(digest);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("生成Token失败");
}
}
}
AuthRealm
权限判断入口,登录的时候只会走 doGetAuthenticationInfo,权限判断的时候才会两个都走
package com.mycsdnshiro.mycsdnshiro.auth;
import com.mycsdnshiro.mycsdnshiro.entity.RoleAndPermission;
import com.mycsdnshiro.mycsdnshiro.entity.User;
import com.mycsdnshiro.mycsdnshiro.entity.UserToken;
import com.mycsdnshiro.mycsdnshiro.service.ShiroLoginService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.List;
@Component
public class AuthRealm extends AuthorizingRealm {
@Autowired
private ShiroLoginService shiroLoginService;
// 授权 获取用户角色和权限
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// 1.从 PrincipalCollection 中获取登录用户的信息
User user = (User) principals.getPrimaryPrincipal();
List users = shiroLoginService.rolePermissionByRoleId(user.getRoleId());
// 2.添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//
Iterator it = users.iterator();
while (it.hasNext()){
RoleAndPermission u=it.next();
simpleAuthorizationInfo.addRole(u.getRoleName());
simpleAuthorizationInfo.addStringPermission(u.getPermission());
System.out.println(u.getPermission());
}
return simpleAuthorizationInfo;
}
// 认证 判断token的有效性
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// 获取token,即前端传入的token
String accessToken=(String)token.getPrincipal();
System.out.println("此处判断token有效性"+accessToken);
// 1.根据accessToken,查询用户信息(token里面就是前端传来的用户名和密码封装后的)findByToken
UserToken tokenEntity = shiroLoginService.findByToken(accessToken);
System.out.println("得到用户实体"+tokenEntity);
// 2.token失败
if(tokenEntity==null||tokenEntity.getExpireTime().isBefore(LocalDateTime.now())){
throw new IncorrectCredentialsException("token,请重新登录");
}
// 3.调用数据库的方法,从数据库中查询 username 对应的用户记录
User user = shiroLoginService.findByUserId(tokenEntity.getUserId());
// 4.若用户不存在,则异常
if(user==null){
throw new UnknownAccountException("用户不存在");
}
// 5.根据用户的情况,来构建 AuthenticationInfo 对象并返回. 通常使用的实现类为: SimpleAuthenticationInfo
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, this.getName());
return info;
}
}
工具类
TokenUtil
获取前端请求体中的token
package com.mycsdnshiro.mycsdnshiro.utils;
import org.apache.commons.lang.StringUtils;
import javax.servlet.http.HttpServletRequest;
/**
* token工具类
*/
public class TokenUtil {
/**
* 获取请求的token
*/
public static String getRequestToken(HttpServletRequest httpRequest) {
//从header中获取token
String token = httpRequest.getHeader("token");
//如果header中不存在token,则从参数中获取token
if (StringUtils.isBlank(token)) {
token = httpRequest.getParameter("token");
}
return token;
}
}
HttpContextUtil
package com.mycsdnshiro.mycsdnshiro.utils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
public class HttpContextUtil {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain(){
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin(){
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}
全局异常处理类
package com.mycsdnshiro.mycsdnshiro.handler;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
/**
* 全局异常处理器
*/
@RestControllerAdvice
public class MyExceptionHandler {
@ExceptionHandler(value = AuthorizationException.class)
public Map handleException(AuthorizationException e) {
//e.printStackTrace();
Map result = new HashMap();
result.put("status", "400");
//获取错误中中括号的内容
String message = e.getMessage();
String msg=message.substring(message.indexOf("[")+1,message.indexOf("]"));
//判断是角色错误还是权限错误
if (message.contains("role")) {
result.put("msg", "对不起,您没有" + msg + "角色");
} else if (message.contains("permission")) {
result.put("msg", "对不起,您没有" + msg + "权限");
} else {
result.put("msg", "对不起,您的权限有误");
}
return result;
}
}
前面没写mapper在这记得扫描
package com.mycsdnshiro.mycsdnshiro;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.mycsdnshiro.mycsdnshiro.mapper")
public class MycsdnshiroApplication {
public static void main(String[] args) {
SpringApplication.run(MycsdnshiroApplication.class, args);
}
}
到此后端代码结束!!!
照着即可,大伙应该都会
至于前端页面样式啥的我随便写写就好,主要是咋用token
再npm install
npm run dev 运行
再加入less
npm install --save-dev [email protected]
element ui
npm install --save element-ui
axios
npm install --save axios
config/index.js
跨域处理
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
// 只改这里---------------前端处理跨域就好了
proxyTable: {
'/api': {
target: 'http://127.0.0.1:9000',
pathRewrite: {
'^/api': ''
}
}
},
// --------------end
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
devtool: 'cheap-module-eval-source-map',
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
productionSourceMap: true,
devtool: '#source-map',
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
bundleAnalyzerReport: process.env.npm_config_report
}
}
main
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import token from './token/token.js'
import API from './api'
Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.prototype.$API=API
Vue.prototype.getToken = token.isLoginToken
Vue.prototype.setToken = token.setToken
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: ' '
})
router.js
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'welcome',
component: ()=>import("@/views/Welcome"),
redirect:'login',
children:[
{
path:'login',
name:'login',
component:()=>import("@/views/Login")
},
{
path:'home',
name:'home',
component:()=>import("@/views/Home")
},
{
path:'adminManage',
name:'adminManage',
component:()=>import("@/views/AdminManage")
},
{
path:'studentManage',
name:'studentManage',
component:()=>import("@/views/StudentManage")
},
{
path:'teacherManage',
name:'teacherManage',
component:()=>import("@/views/TeacherManage")
},
]
}
]
})
token.js
import vue from 'vue'
import router from '../router'
import ElementUI from 'element-ui'
vue.use(ElementUI)
import { Message } from 'element-ui';
const isLoginToken = () => {
const token = localStorage.getItem("token");
if (!token) {
Message({
showClose: true,
message: "请先登录",
type: "error",
duration: "3000"
});
router.push({ path: "/login" });
}
return token;
}
const setToken = (token) => {
localStorage.setItem("token", token);
}
export default {
isLoginToken,
setToken
}
login.js
import axios from "axios";
const login = (loginInfo) => {
console.log(loginInfo);
return axios.post('api/user/login', {
...loginInfo
})
}
export default {
login,
}
base.js
import axios from 'axios'
const goAdmin = (token) => {
return axios.get('api/base/admin', {
headers: {
token: token
}
})
}
const goTeacher = (token) => {
return axios.get('api/base/teacher', {
headers: {
token: token
}
})
}
const goStudent = (token) => {
return axios.get('api/base/student', {
headers: {
token: token
}
})
}
const goInsert = (token) => {
return axios.post('/api/base/insert', {}, {
headers:{
token:token
}
})
}
const goUpdate = (token) => {
return axios.put('api/base/update', {}, {
headers:{
token:token
}
})
}
const goDelete = (token) => {
return axios.delete('api/base/delete', {
headers: {
token: token
}
})
}
const goSelect = (token) => {
console.log(token);
return axios.get('api/base/select', {
headers: {
token: token
}
})
}
export default {
goAdmin,
goTeacher,
goStudent,
goInsert,
goDelete,
goSelect,
goUpdate
}
index.js
import login from '@/api/login/login.js'
import base from '@/api/base/base.js'
export default{
login,
base
}
Welcome.vue
Login.vue
登录
提交
Home.vue
权限检验页面
本文章参考:这位博主使用的是Jpa一些和其它的技术,我是用了mybatis并进行一些修改
一看就懂!Springboot +Shiro +VUE 前后端分离式权限管理系统_大誌的博客-CSDN博客_shiro前后端分离