前言:
shiro是Apache下的一款开源安全框架,相对于springSecurity来说 它功能可能没有那么完善 但它更简单更容易上手,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的 Shiro 就足够了。对于它俩到底哪个好,这个不必纠结,能更简单的解决项目问题就足够了
官方架构图:
常用功能:
Subject: 当前访问的用户(主体,一般指用户)
SecurityManager:安全管理器,管理所有Subject,可以配合内部安全组件。(类似于SpringMVC中的DispatcherServlet)
Realms:用于进行权限信息的验证,一般需要自己实现。
Authentication: 身份认证/登录 认证用户是否有相应的身份
Authorization: 授权,权限验证 验证以认证的用户是否具有相应权限,即判断该用户是否有权限做某件事
Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通 JavaSE 环境的,也可以是如 Web 环境的;
Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储
Remember Me:记住我,登录后,下次再来的话不用登录了。
如何整合:
项目结构:
1、导入依赖
<!--shiro权限框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.5.3</version>
</dependency>
2、编写我们的pojo(User,Role,Permission)
User:
package com.shenwang.pojo;
import java.io.Serializable;
import java.util.Set;
/**
* @author: shenwang
* Date: 2021/6/21
*/
public class User implements Serializable {
/**
* 编号 主键 唯一标识
*/
private Integer id;
/**
* 用户名
*/
private String userName;
/**
* 密码
*/
private String password;
/**
* 对应的角色集合
*/
private Set<Role> roleSet;
public User() {
}
public User(Integer id, String userName, String password, Set<Role> roleSet) {
this.id = id;
this.userName = userName;
this.password = password;
this.roleSet = roleSet;
}
public Integer getId() {
return id;
}
public void setId(Integer 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> getRoleSet() {
return roleSet;
}
public void setRoleSet(Set<Role> roleSet) {
this.roleSet = roleSet;
}
}
Role:
package com.shenwang.pojo;
import java.io.Serializable;
import java.util.Set;
/**
* @author: shenwang
* Date: 2021/6/21
*/
public class Role implements Serializable {
/**
* 主键 唯一标识
*/
private Integer id;
/**
* 角色名称
*/
private String roleName;
/**
* 对应的权限集合
*/
private Set<Permission> permissionSet;
public Role() {
}
public Role(Integer id, String roleName, Set<Permission> permissionSet) {
this.id = id;
this.roleName = roleName;
this.permissionSet = permissionSet;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public Set<Permission> getPermissionSet() {
return permissionSet;
}
public void setPermissionSet(Set<Permission> permissionSet) {
this.permissionSet = permissionSet;
}
}
Permission:
package com.shenwang.pojo;
import java.io.Serializable;
/**
* @author: shenwang
* Date: 2021/6/21
*/
public class Permission implements Serializable {
/**
* id 主键 唯一标识
*/
private Integer id;
/**
* 权限名称
*/
private String permissionName;
public Permission() {
}
public Permission(Integer id, String permissionName) {
this.id = id;
this.permissionName = permissionName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPermissionName() {
return permissionName;
}
public void setPermissionName(String permissionName) {
this.permissionName = permissionName;
}
}
2、编写service层
在这里我们用的是假数据 模拟查询数据库 所以就没有编写dao层代码,有时间的小伙伴可以去连接数据库查询数据,这里我们直接编写Service
LoginService:
package com.shenwang.service;
import com.shenwang.pojo.User;
/**
* @author: shenwang
* Date: 2021/6/21
*/
public interface LoginService {
/**
* 模拟数据库查询
* @param userName
* @return
*/
User getUserByName(String userName);
}
LoginServiceImpl:
package com.shenwang.service.impl;
import com.shenwang.pojo.Permission;
import com.shenwang.pojo.Role;
import com.shenwang.pojo.User;
import com.shenwang.service.LoginService;
import org.springframework.stereotype.Service;
import java.security.Permissions;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* @author: shenwang
* Date: 2021/6/21
*/
@Service("loginService")
public class LoginServiceImpl implements LoginService {
/**
* 模拟数据库查询
*
* @param userName
* @return
*/
@Override
public User getUserByName(String userName) {
Permission permissions1 = new Permission(1,"query");
Permission permissions2 = new Permission(2,"add");
Set<Permission> permissionsSet = new HashSet<>();
permissionsSet.add(permissions1);
permissionsSet.add(permissions2);
Role role = new Role(1, "admin", permissionsSet);
Set<Role> roleSet = new HashSet<>();
roleSet.add(role);
User user = new User(1, "shenwang", "123", roleSet);
Map<String, User> map = new HashMap<>();
map.put(user.getUserName(), user);
Set<Permission> permissionsSet1 = new HashSet<>();
permissionsSet1.add(permissions1);
Role role1 = new Role(1, "user", permissionsSet1);
Set<Role> roleSet1 = new HashSet<>();
roleSet1.add(role1);
User user1 = new User(1, "zhangsan", "123456", roleSet1);
map.put(user1.getUserName(), user1);
return map.get(userName);
}
}
3、自定义Realm
package com.shenwang.shiro;
import com.shenwang.pojo.Permission;
import com.shenwang.pojo.Role;
import com.shenwang.pojo.User;
import com.shenwang.service.LoginService;
import org.apache.shiro.authc.AuthenticationException;
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 org.thymeleaf.util.StringUtils;
/**
* @author: shenwang
* Date: 2021/6/21
*/
public class CustomRealm extends AuthorizingRealm {
@Autowired
private LoginService loginService;
/**
*授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取登录用户名
String userName = (String) principalCollection.getPrimaryPrincipal();
//根据用户名称查询用户
User user = loginService.getUserByName(userName);
//添加角色和权限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role:user.getRoleSet())
{
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
//添加权限
for(Permission permission:role.getPermissionSet())
{
simpleAuthorizationInfo.addStringPermission(permission.getPermissionName());
}
}
return simpleAuthorizationInfo;
}
/**
*认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if(StringUtils.isEmpty(authenticationToken.getPrincipal().toString()))
{
return null;
}
//获取用户信息
String userName = authenticationToken.getPrincipal().toString();
User user = loginService.getUserByName(userName);
if(user==null)
{
return null;
}
else
{
//这里验证authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName, user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
4、编写配置类(ShiroConfig.java)
package com.shenwang.config;
import com.shenwang.shiro.CustomRealm;
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 java.util.HashMap;
import java.util.Map;
/**
* @author: shenwang
* Date: 2021/6/21
*/
@Configuration
public class ShiroConfig {
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
//将自己的验证方式加入容器
@Bean
public CustomRealm myShiroRealm() {
CustomRealm customRealm = new CustomRealm();
return customRealm;
}
//权限管理,配置主要是Realm的管理认证
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工厂,设置对应的过滤条件和跳转条件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager 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(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
5、编写控制层LoginController
package com.shenwang.controller;
import com.shenwang.pojo.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UnknownAccountException;
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.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author: shenwang
* Date: 2021/6/21
*/
@RestController
public class LoginController {
/**
* 登录
* @param user
* @return
*/
@GetMapping("login")
public String login(User user)
{
if (StringUtils.isEmpty(user.getUserName()) || StringUtils.isEmpty(user.getPassword())) {
return "请输入用户名和密码!";
}
//用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
user.getUserName(),
user.getPassword()
);
try {
//进行验证,这里可以捕获异常,然后返回对应信息
subject.login(usernamePasswordToken);
} catch (UnknownAccountException e) {
return "用户名不存在!";
} catch (AuthenticationException e) {
return "账号或密码错误!";
} catch (AuthorizationException e) {
return "没有权限";
}
return "login success";
}
@RequiresRoles("admin")
@GetMapping("/admin")
public String admin() {
return "admin success!";
}
@RequiresPermissions("query")
@GetMapping("/index")
public String index() {
return "index success!";
}
@RequiresPermissions("add")
@GetMapping("/add")
public String add() {
return "add success!";
}
}
最后我们的springboot整合shiro就完成啦!但是没有连接数据库,需要整合数据库的小小伙伴可以参考我之前的博客哟~