1.配置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.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>shiro</artifactId>
<version>1.0</version>
<packaging>war</packaging>
<name>shiro</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!-- thymeleaf依赖包 -->
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- SpringBoot的Mybatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<!--tomcat插件-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<!--测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Shiro整合包 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.项目文件结构
3.ShiroController
package com.example.shiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ShiroCotroller {
/**
* 登录逻辑处理
* @param name
* @param password
*/
@RequestMapping("/login")
public String login(String name,String password){
/**
* 使用Shiro编写认证操作
*/
//1.获取Subject
Subject subject = SecurityUtils.getSubject();
//2.封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//3.执行登录方法
try {
subject.login(token);
return "redirect:toIndex";
}
catch (Exception e){
System.out.println("登录异常");
return "pages/login";
}
}
/**
* 前往登录页面
* @return
*/
@RequestMapping("/toLogin")
public String toLogin(){
return "pages/login";
}
/**
* 前往index页面
* @return
*/
@RequestMapping("toIndex")
public String toIndex(){
return "index";
}
}
4.ShiroConfig
package com.example.shiro.security;
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;
/**
* 配置类,代替xml文件
*/
@Configuration
public class ShiroConfig {
/**
* 获取自定义Relam
*/
// 交给Spring的IOC容器管理
@Bean(name = "userRealm")
public UserRealm getRealm(){
UserRealm realm = new UserRealm();
// 关联自定义加密
realm.setCredentialsMatcher(hashedCredentialsMatcher());
return realm;
}
/**
* 配置加密属性
* @return
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(2);
//true加密用的hex编码,false用的base64编码,要和注册时的一致
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
/**
* 创建DefaultWebSecurityManager
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
// @Qualifier("") 传入的参数要与getRealm()的@Bean的name属性一致
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* 创建ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
// @Qualifier的值也要对应上
// 创建对象
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
// 关联securityManager
filterFactoryBean.setSecurityManager(securityManager);
// 添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* roles: 该资源必须得到角色权限才可以访问
*/
// 为了保证配置的次序,使用LinkedHashMap
Map<String,String> filterMap = new LinkedHashMap<>();
// 放行登录请求
filterMap.put("/login","anon");
//拦截其余所有请求
filterMap.put("/**","authc");
//授权过滤器
//注意:当前授权拦截后,shiro会自动跳转到未授权页面
filterMap.put("/add", "roles[admin]");
filterMap.put("/update", "roles[admin]");
filterFactoryBean.setFilterChainDefinitionMap(filterMap);
// 修改跳转的登录页面
filterFactoryBean.setLoginUrl("/toLogin");
//设置未授权提示uri
filterFactoryBean.setUnauthorizedUrl("/noAuth");
return filterFactoryBean;
}
}
5.自定义Realm
package com.example.shiro.security;
import com.example.shiro.pojo.User;
import com.example.shiro.service.ShiroService;
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.crypto.hash.SimpleHash;
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 javax.annotation.Resource;
import java.util.HashSet;
import java.util.Set;
/**
* 自定义Realm
*/
public class UserRealm extends AuthorizingRealm {
@Resource
private ShiroService shiroService;
/**
* 授权
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//1. 从 PrincipalCollection 中来获取登录用户的信息
Object principal = principalCollection.getPrimaryPrincipal();
//2. 利用登录的用户的信息来用户当前用户的角色或权限(需要查询数据库)
Set<String> roles = new HashSet<>();
// 假设查询到是管理员
String role = "admin";
roles.add(role);
//3. 创建 SimpleAuthorizationInfo, 并设置其 roles 属性.
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(roles);
//4. 返回 SimpleAuthorizationInfo 对象.
return info;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
/**
* 这个方法默认会有一个参数,这个参数是controller层传来的用户信息
*/
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String name = token.getUsername();
// 创建对象
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo();
// 在数据库查询数据
User user = this.shiroService.selByName(name);
// 判空
if (user == null)
{
System.out.println("用户不存在");
return null;//shiro底层会抛出UnKnowAccountException
}
// 加密的盐值(假设就是用户名)
ByteSource salt = ByteSource.Util.bytes(name);
// 返回认证信息getName是调用的父类的方法
return new SimpleAuthenticationInfo(user.getName(),user.getPassword(),salt,getName());
}
/**
* 测试方法,计算MD5加密后密码(这里面的代码在认证授权时没有用,可以在用户注册时使用)
* @param args
*/
public static void main(String[] args) {
// 加密算法
String hashAlgorithmName = "MD5";
// 要加密的密码
Object credentials = "1234";
// 盐值(一般为用户名)
Object salt = "meng";
// 加密次数
int hashIterations = 2;
Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
// 这里有两种方式获取密码 toBase64 和 toHex 一定要和Shiro的配置一致
System.out.println(((SimpleHash) result).toHex());
}
}
推荐文章:
Spring Boot整合Shiro进行权限授权管理:https://blog.csdn.net/sunming709424/article/details/80094794
凭证器详解:https://blog.csdn.net/New_Yao/article/details/100562044