Spring Boot学习笔记(二十一)Spring Boot Shiro 实例
日期: 2019-11-05 分类: 跨站数据测试 313次阅读
Spring Boot学习笔记(二十一)Spring Boot Shiro 实例
放代码ing~ 搬知识ing~
新建项目
项目结构
pom.xml 需要的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
pom.xml 文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>wen</groupId>
<artifactId>shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
我们一般都会给用户 (User) 角色(Role: 管理者、使用者)设置用户权限(Permission: 读和写、只读),所以先建实体类 Permission.java:
package wen.shiro.bean;
public class Permission {
private Long id;
private String permission;
public Permission(Long id, String permission) {
this.id = id;
this.permission = permission;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
}
Role.java,其中有对于权限的集合Set,即只读权限的用户添加一个Permission,读和写的用户添加两个权限:
package wen.shiro.bean;
import java.util.Set;
public class Role {
private Long id;
private String role;
private Set<Permission> permissions;
public Role(Long id, String role, Set<Permission> permissions) {
this.id = id;
this.role = role;
this.permissions = permissions;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public Set<Permission> getPermissions() {
return permissions;
}
public void setPermissions(Set<Permission> permissions) {
this.permissions = permissions;
}
}
User.java 用户类,同样地,一个用户类有Role的集合Set,一个用户可以有多个角色(管理者和访问者:添加两个Role,访问者:添加一个Role)
package wen.shiro.bean;
import java.util.Set;
public class User {
private Long id;
private String username;
private String password;
private Set<Role> roles;
public User(Long id, String username, String password, Set<Role> roles) {
this.id = id;
this.username = username;
this.password = password;
this.roles = roles;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
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 Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
}
LoginService.java 和 对应实现类 LoginServiceImpl.java
package wen.shiro.service;
import wen.shiro.bean.User;
public interface LoginService {
User getUserByUsername(String username);
}
package wen.shiro.service.impl;
import org.springframework.stereotype.Service;
import wen.shiro.bean.Permission;
import wen.shiro.bean.Role;
import wen.shiro.bean.User;
import wen.shiro.service.LoginService;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@Service
public class LoginServiceImpl implements LoginService {
Map<String, User> map = new HashMap<>();
public LoginServiceImpl() {
// 设置用户权限:query 和 add
Permission queryPermission = new Permission(1L, "query");
Permission addPermission = new Permission(2L, "add");
// 用户一角色权限设置:读和写
Set<Permission> permissionSetOne = new HashSet<>();
permissionSetOne.add(queryPermission);
permissionSetOne.add(addPermission);
Role role = new Role(1L, "admin", permissionSetOne);
Set<Role> roleSet = new HashSet<>();
roleSet.add(role);
User user = new User(1L, "admin_user", "123456", roleSet);
map.put(user.getUsername(), user);
// 用户二角色权限设置:只读权限
Set<Permission> permissionSetTwo = new HashSet<>();
permissionSetTwo.add(queryPermission);
Role role1 = new Role(2L, "user", permissionSetTwo);
Set<Role> roleSet1 = new HashSet<>();
roleSet1.add(role1);
User user1 = new User(2L, "visitor_user", "123456", roleSet1);
map.put(user1.getUsername(), user1);
}
@Override
public User getUserByUsername(String username) {
return map.get(username);
}
}
接下来需要定义一个Realm类用于查询用户的角色和权限信息并记录到权限管理器SercurityManager
Realm.java
package wen.shiro.realm;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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 wen.shiro.bean.Permission;
import wen.shiro.bean.Role;
import wen.shiro.bean.User;
import wen.shiro.service.LoginService;
public class CustomRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 获取登录用户名
String username = (String) principalCollection.getPrimaryPrincipal();
// 根据用户名查询信息
User user = loginService.getUserByUsername(username);
// 添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role : user.getRoles()) {
// 添加角色
simpleAuthorizationInfo.addRole(role.getRole());
// 添加权限
for (Permission permissions : role.getPermissions()) {
simpleAuthorizationInfo.addStringPermission(permissions.getPermission());
}
}
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {
// 在post请求之前先进行认证
if (authenticationToken.getPrincipal() == null) {
return null;
}
// 获取用户信息
String username = authenticationToken.getPrincipal().toString();
User user = loginService.getUserByUsername(username);
if (user == null) {
// 返回后会报出异常
return null;
} else {
//验证 authenticationToken和 simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo
= new SimpleAuthenticationInfo(username, user.getPassword(), getName());
return simpleAuthenticationInfo;
}
}
}
到这里已经实现了用户认证模块,那么我们接下来要做的就是把Realm和权限管理器SercurityManager放入Spring容器:
ShiroConfig.java
package wen.shiro.config;
import org.apache.shiro.mgt.SecurityManager;
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.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import wen.shiro.realm.CustomRealm;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
// 将自定义的验证方式加入容器
@Bean
public CustomRealm shiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
// 权限管理:Realm的管理认证
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm());
return securityManager;
}
// Filter 工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
// 登出
map.put("/logout", "logout");
// 对所有用户进行认证
map.put("/**", "authc");
// 登录
shiroFilterFactoryBean.setLoginUrl("/login");
// 首页
shiroFilterFactoryBean.setSuccessUrl("/index");
// 认证失败不跳转
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
LoginController.java
package wen.shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import wen.shiro.bean.User;
@RestController
public class LoginController {
@RequestMapping("/login")
public String login(User user) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(usernamePasswordToken);
} catch (AuthenticationException e) {
e.printStackTrace();
return "账号或密码错误!";
} catch (AuthorizationException e) {
e.printStackTrace();
return "没有权限";
}
return "login success";
}
@RequiresRoles("admin")
@RequiresPermissions("add")
@RequestMapping("/index")
public String index() {
return "This is the index after you've been authorized";
}
}
注:如果现在直接开启项目访问浏览器而用户名或密码错误,也就是没有验证成功的话,控制台会报错,但是前端是看不到任何报错信息的,所以在这里定义一个用于拦截异常的类,用于返回给前端错误信息。
ExceptionCatcher.java
package wen.shiro.filter;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@ControllerAdvice
@Slf4j
public class ExceptionCatcher {
@ExceptionHandler
@ResponseBody
public String ErrorHandler(AuthorizationException e) {
return "没有通过权限验证";
}
}
开启项目访问 http://localhost:8080/login?username=admin_user&password=123456
(在LoginServiceImpl.java 定义了用户名为admin_user, 密码为123456)
接着访问 http://localhost:8080/index
在之前的 LoginController.java 中:
@RequiresRoles("admin")
@RequiresPermissions("add")
@RequestMapping("/index")
public String index() {
return "This is the index after you've been authorized";
}
注解 @RequiresRoles("admin")
说明只有管理员才能得到返回 “This is the index after you’ve been authorized” 的结果,所以我们试一下 以visitor_user 的角色访问
http://localhost:8080/login?username=visitor_user&password=123456
接着再访问 http://localhost:8080/index
基本功能完成 😉 ~
源码:https://github.com/StephaineWYT/shiro
除特别声明,本站所有文章均为原创,如需转载请以超级链接形式注明出处:SmartCat's Blog
精华推荐