目录
第六步:实现自定义的AuthenticationToken。
第八步:实现自定义AuthenticatingFilter。
前言
前段日子写过一篇关于SpringBoot+Shiro的简单整合的例子,那个例子并不适用于我们目前的前后端分离开发的趋势。我之前写过一个项目也是用到了Shiro的前后端分离,某度了许久也没找到解决方案,什么去掉shiroFilter.setLoginUrl();也阻止不了讨人厌的login.jsp的出现。直到我看到了renren-fast的源码...废话不多说,让我们来看看如何实现吧!
前后端分离
要实现前后端分离,需要考虑以下2个问题: 1. 项目不再基于session了,如何知道访问者是谁? 2. 如何确认访问者的权限?
前后端分离,一般都是通过token实现,本项目也是一样;用户登录时,生成token及 token过期时间,token与用户是一一对应关系,调用接口的时候,把token放到header或 请求参数中,服务端就知道是谁在调用接口。
代码已上传到Git:
后台代码:https://github.com/FENGZHIJIE1998/shiro-auth
前端代码:https://github.com/FENGZHIJIE1998/shiro-vue
觉得好用的记得点个Star哦
Let's do it!!
介绍:这次我们使用Shiro快速搭建前后端分离的权限管理系统 利用JPA帮我们管理数据库,Swagger Knife4j 帮我搭建Web测试环境;
后台基于 Springboot JPA Knife4j Shiro
前端基于 VUE ElementUI
注意:主要观察token的使用方法!
第一步:新建工程
pom文件application.yml巴拉巴拉这里省略,这里贴出需要用到的依赖:
<!--starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--validation-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--JPA-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!--JDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!--mysql-connector-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- druid-spring-boot-starter -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!-- swagger -->
<dependency>
<groupId>com.spring4all</groupId>
<artifactId>swagger-spring-boot-starter</artifactId>
<version>1.8.0.RELEASE</version>
</dependency>
<!-- knife4j -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
<!-- commons-lang -->
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
第二步:准备好要用的包包和类类
第三步:编写登陆入口
为了方便这里不做密码加盐加密:
/**
* @Author 大誌
* @Date 2019/3/30 22:04
* @Version 1.0
*/
@RestController
public class ShiroController {
private final ShiroService shiroService;
public ShiroController(ShiroService shiroService) {
this.shiroService = shiroService;
}
/**
* 登录
*/
@ApiOperation(value = "登陆", notes = "参数:用户名 密码")
@PostMapping("/sys/login")
public Map<String, Object> login(@RequestBody @Validated LoginDTO loginDTO, BindingResult bindingResult) {
Map<String, Object> 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();
//用户信息
User user = shiroService.findByUsername(username);
//账号不存在、密码错误
if (user == null || !user.getPassword().equals(password)) {
result.put("status", 400);
result.put("msg", "账号或密码有误");
} else {
//生成token,并保存到数据库
result = shiroService.createToken(user.getUserId());
result.put("status", 200);
result.put("msg", "登陆成功");
}
return result;
}
/**
* 退出
*/
@ApiOperation(value = "登出", notes = "参数:token")
@PostMapping("/sys/logout")
public Map<String, Object> logout(@RequestHeader("token")String token) {
Map<String, Object> result = new HashMap<>();
shiroService.logout(token);
result.put("status", 200);
result.put("msg", "您已安全退出系统");
return result;
}
}
第四步:编写ShiroService中的方法
主要是生成一个token返回给前端。
/**
* @Author 大誌
* @Date 2019/3/30 22:18
* @Version 1.0
*/
@Service
public class ShiroServiceImpl implements ShiroService {
@Autowired
private UserRepository userRepository;
@Autowired
private SysTokenRepository sysTokenRepository;
/**
* 根据username查找用户
*
* @param username
* @return User
*/
@Override
public User findByUsername(String username) {
User user = userRepository.findByUsername(username);
return user;
}
//12小时后过期
private final static int EXPIRE = 3600 * 12;
@Override
/**
* 生成一个token
*@param [userId]
*@return Result
*/
public Map<String, Object> createToken(Integer userId) {
Map<String, Objec