SpringBoot 整合 Shiro(完整版),java 学习网站
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2、项目结构
3、配置链接数据库属性
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.hibernate.use-new-id-generator-mappings=false
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
server.port=80
server.servlet.context-path=/shiro
1、编写实体类
菜单表实体类 TbMenu,Spring-Data-Jpa 可以根据实体类去数据库新建或更新对应的表结构,详情可以访问Spring-Data-Jpa入门
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.data.annotation.CreatedBy;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
菜单表
@author
*/
@Entity
public class TbMenu {
private Integer id;
private String name;
private String url;
private Integer idx;
@JsonIgnore
private TbMenu parent;
@JsonIgnore
private List<TbMenu> children = new ArrayList<>();
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(unique = true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Integer getIdx() {
return idx;
}
public void setIdx(Integer idx) {
this.idx = idx;
}
@ManyToOne
@CreatedBy
public TbMenu getParent() {
return parent;
}
public void setParent(TbMenu parent) {
this.parent = parent;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
@OrderBy(value = "idx")
public List<TbMenu> getChildren() {
return children;
}
public void setChildren(List<TbMenu> children) {
this.children = children;
}
}
角色及权限表 SysRole,parent 为 null 时为角色,不为 null 时为权限。
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.data.annotation.CreatedBy;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
/***
角色及角色对应的菜单权限
@author
*parent 为 null 时为角色,不为 null 时为权限
*/
public class SysRole {
private Integer id;
private String name; //名称
@JsonIgnore
private SysRole parent;
private Integer idx; //排序
@JsonIgnore
private List<SysRole> children = new ArrayList<>();
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length = 20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToOne
@CreatedBy
public SysRole getParent() {
return parent;
}
public void setParent(SysRole parent) {
this.parent = parent;
}
@OneToMany(cascade = CascadeType.ALL, mappedBy = "parent")
public List<SysRole> getChildren() {
return children;
}
public void setChildren(List<SysRole> children) {
this.children = children;
}
public Integer getIdx() {
return idx;
}
public void setIdx(Integer idx) {
this.idx = idx;
}
}
最后实现的就是用户管理了,只需要对添加的用户分配对应的角色就可以了,用户登录时,显示角色对应的权限。
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
/**
用户表
*/
@Entity
public class SysUser {
private Integer id;
private String username; //账号
private String password; //密码
private String name; //姓名
@JsonIgnore
private List<SysRole> roles = new ArrayList<>(); //角色
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@Column(length = 20, unique = true)
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Column(length = 100)
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Column(length = 20)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@ManyToMany(cascade = CascadeType.REFRESH, fetch = FetchType.EAGER)
@JoinTable(name = "sys_user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
}
2、Shiro 配置类
Shiro 配置类 ShiroConfig。
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
@description Shiro 配置类
*/
@Configuration
public class ShiroConfig {
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为 MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean("userRealm")
public UserRealm userRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
UserRealm userRealm = new UserRealm();
userRealm.setCredentialsMatcher(matcher);
return userRealm;
}
@Bean
public ShiroFilterFactoryBean shirFilter(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 设置 SecurityManager,安全管理器
bean.setSecurityManager(securityManager);
// 设置登录跳转页面
bean.setLoginUrl("/index");
// 设置未授权提示页面(没有权限访问后的页面)
bean.setUnauthorizedUrl("/unAuth");
/**
Shiro 内置过滤器,可以实现拦截器相关的拦截器
常用的过滤器:
**/
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/login","anon");
filterMap.put("/user","authc");
filterMap.put("/system","perms[system]");
filterMap.put("/static/**","anon");
filterMap.put("/**","authc");
filterMap.put("/logout", "logout");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
/**
注入 securityManager
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(HashedCredentialsMatcher hashedCredentialsMatcher) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联 realm.
securityManager.setRealm(userRealm(hashedCredentialsMatcher));
return securityManager;
}
}
自定义 Realm 用于查询用户的角色和权限信息并保存到权限管理器中。代码如下:
import com.mcy.springbootshiro.entity.SysRole;
import com.mcy.springbootshiro.entity.SysUser;
import com.mcy.springbootshiro.service.SysUserService;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class UserRealm extends AuthorizingRealm {
@Autowired
private SysUserService userService;
/**
授权逻辑方法
**/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权");
//获取当前登录对象
Subject subject = SecurityUtils.getSubject();
SysUser user = (SysUser)subject.getPrincipal();
if(user != null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 角色与权限字符串集合
Collection<String> rolesCollection = new HashSet<>();
//获得当前用户的角色
List<SysRole> roles = user.getRoles();
for(SysRole role : roles){
rolesCollection.add(role.getName());
}
//添加当前用户的角色权限,用于判断可以访问那些功能
info.addStringPermissions(rolesCollection);
return info;
}
return null;
}
/**
认证
**/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证");
// 编写 shiro 判断逻辑,判断用户名和密码
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
// 判断用户名
SysUser bean = userService.findByUsername(token.getUsername());
//用户名不存在
if(bean == null){
throw new UnknownAccountException();
}
//以账号作为加密的盐值
ByteSource credentialsSalt = ByteSourc
e.Util.bytes(bean.getUsername());
// 判断密码,
return new SimpleAuthenticationInfo(bean, bean.getPassword(),
credentialsSalt, getName());
}
}
其中密码加密使用的是 MD5 加密,加密代码如下:
public static void main(String[] args){
String hashAlgorithName = "MD5";
//加密密码
String password = "123456";
//加密次数
int hashIterations = 1024;
//账号作为加密的盐值
ByteSource credentialsSalt = ByteSource.Util.bytes("admin");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
3、编写控制器
新建 IndexController 测试控制器。这里持久层框架使用的是 SpringDataJpa,查询只需遵循查询方法命名规范即可,所以这里就直接写控制器代码了。想进一步了解 SpringDataJpa 的小伙伴,请移步:Spring-Data-Jpa入门。
IndexController 控制器代码如下:
import com.mcy.springbootshiro.entity.SysRole;
import com.mcy.springbootshiro.entity.SysUser;
import com.mcy.springbootshiro.service.SysRoleService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@Autowired
private SysRoleService roleService;
@RequestMapping({"/index", "/"})
public String index(){
return "login";
}
@RequestMapping(value = "/login")
public String login(String username, String password, Model model){
//1.获取 subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
//3.执行登录方法
subject.login(token);
//登录成功
return "redirect:/mian";
} catch (UnknownAccountException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
//e.printStackTrace();
//登录失败:用户名不存在
model.addAttribute("msg", "密码输入有误");
return "login";
}
}
@RequestMapping(value = "/mian")
public String main(){
SysUser user = (SysUser) SecurityUtils.getSubject().getPrincipal();
System.out.println(user.getName());
//登录成功后,输出对应的角色和菜单
for(SysRole role: user.getRoles()){
System.out.println(role.getName()+"=====角色");
for(SysRole roles : role.getChildren()){
System.out.println(roles.getName()+"===="+role.getName()+"角色对应的菜单");
}
}
return "main";
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
if (subject != null) {
subject.logout();
}
return "redirect:/main";
}
@RequestMapping("/unAuth")
public String unAuth(){
return "unAuth";
}
@RequestMapping("/system")
public String system(){
return "system";
}
@RequestMapping("/user")
public String user(){
return "user";
}
}
评论