SpringBoot整合Shiro:完成认证授权
1.技术SpringBoot+Mybatis+Maven+Shiro+Mysql
1.1:创建SpringBoot项目 当前我没有网络 所以是手动创建maven 可以去搜一下如何 自动创建SpringBoot项目
2:把以下内容复制到pom.xml文件中
<!--springboot基础的创建-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<smybatis.version>2.1.0</smybatis.version>
</properties>
<dependencies>
<!--SpringBook所需的jar包 默认不需要写版本号 继承父类的版本号-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--springboot整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${smybatis.version}</version>
</dependency>
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>RELEASE</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--上传文件的-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<!--添加shiro安全框架-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.4.0</version>
</dependency>
<!--shiro自定义配置需要的插件-->
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.3</version>
</dependency>
<!--shiro-spring整合-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.0</version>
</dependency>
<!--shiro缓存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.3</version>
</dependency>
<!--shiro和Thyemy整合 可以使用shiro的标签-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
<!--实现SpringBoot热部署配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
<!--spirng 打包成war包-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<includeEmptyDirectories>true</includeEmptyDirectories>
</configuration>
</plugin>
</plugins>
</build>
3:然后我们在项目中创建这几个文件夹 至这几个文件夹干什么我都不细说了
4:在application.yml配置文件配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://127.0.0.1:3306/books?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true
entity:
package com.example.springboot.entity;
import javax.persistence.Entity;
import javax.persistence.Table;
@Table(name = "userinfo")
public class UserInfo {
private Integer userid;
private String username;
private String userpassword;
private String salt;
private Integer hashinteration;
@Override
public String toString() {
return "UserInfo{" +
"userid=" + userid +
", username='" + username + '\'' +
", userpassword='" + userpassword + '\'' +
", salt='" + salt + '\'' +
", hashinteration=" + hashinteration +
'}';
}
public Integer getUserid() {
return userid;
}
public void setUserid(Integer userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserpassword() {
return userpassword;
}
public void setUserpassword(String userpassword) {
this.userpassword = userpassword;
}
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}
public Integer getHashinteration() {
return hashinteration;
}
public void setHashinteration(Integer hashinteration) {
this.hashinteration = hashinteration;
}
}
dao层:
package com.example.springboot.dao;
import com.example.springboot.entity.UserInfo;
import org.apache.ibatis.annotations.Select;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
public interface UserInfoDAO extends Mapper<UserInfo> {
//根据用户输入的用户名到数据库进行查询
@Select("select *from userinfo where username=#{param1}")
UserInfo FindByUname(String username);
//获取当前用户的角色
@Select("select roleName from roleinfo where uid in(select userid from userinfo where username=#{param1})")
List<String> findRole(String username);
//根据角色名称获取当前的权限
@Select("select jurname from jurinfo where rid in (select roleid from roleinfo where rolename=#{param1})")
List<String> findJure(String roleName);
}
service层:
package com.example.springboot.service;
import com.example.springboot.dao.UserInfoDAO;
import com.example.springboot.entity.UserInfo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
@Service
public class UserInfoService {
@Resource
UserInfoDAO userInfoDAO;
//在数据库用md5插入一条加密的密码
public int InsertUserInfo(UserInfo userInfo)
{
userInfo.setSalt("csdn");
userInfo.setHashinteration(5);
return userInfoDAO.insert(userInfo);
}
//登录
public UserInfo findUserName(String username)
{
return userInfoDAO.FindByUname(username);
}
//根据当前用户获取角色
public List<String> findRole(String username)
{
return userInfoDAO.findRole(username);
}
//根据当前角色名称获取对应的权限
public List<String> findJure(String roleName)
{
return userInfoDAO.findJure(roleName);
}
}
shiro层:一个shiroconfig 一个myreamle
1: shiroconfig
package com.example.springboot.shiro;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
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;
@Configuration
public class ShiroConfig {
//添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器:
* anon: 无需认证(登录)可以访问
* authc: 必须认证才可以访问
* user: 如果使用rememberMe的功能可以直接访问
* perms: 该资源必须得到资源权限才可以访问
* role: 该资源必须得到角色权限才可以访问
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//LinkedHashMap 有顺序的存 取出来的也是按顺序取
LinkedHashMap<String, String> map = new LinkedHashMap<>();
//登录提交不需要认证
map.put("/login_submit","anon");
//static下面的静态资源不需要拦截
map.put("/static/**","authc");
//设置要登录的页面
shiroFilterFactoryBean.setLoginUrl("/login_show");
//其他页面都要认证
map.put("/*/**","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("myRealm") MyRealm myRealm)
{
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(myRealm);
return defaultWebSecurityManager;
}
/**
* 自定义Realm
* @return
*/
@Bean(name = "myRealm")
public MyRealm myRealm(@Qualifier("CredentialsMatcher") HashedCredentialsMatcher credentialsMatcher)
{
MyRealm myRealm=new MyRealm();
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
/**
* 设置加密对象,加密规则
*/
@Bean(name = "CredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher()
{
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//使用md5加密
hashedCredentialsMatcher.setHashAlgorithmName("md5");
//加密次数
hashedCredentialsMatcher.setHashIterations(5);
return hashedCredentialsMatcher;
}
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
*/
@Bean
public ShiroDialect shiroDialect()
{
return new ShiroDialect();
}
}
2:myreamle
package com.example.springboot.shiro;
import com.example.springboot.entity.UserInfo;
import com.example.springboot.service.UserInfoService;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyRealm extends AuthorizingRealm {
@Autowired
UserInfoService userInfoService;
/**
*
* @param principalCollection
* @return 进行授权操作
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//防止报错 加一个Try catch
try{
UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal();
//根据当前用户查询数据库角色
List<String> role = userInfoService.findRole(userInfo.getUsername());
if(role.size()!=0)
{
//创建一个Set把角色存起来
Set<String> roles=new HashSet<>();
for(String r:role)
{
roles.add(r);
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(roles);
//根据角色分配权限
//定义一个列表用来放权限
ArrayList<String> permissions = new ArrayList<>();
for (String op:roles)
{
//根据用户的角色来查询对应的权限
List<String> jure = userInfoService.findJure(op);
if(jure.size()!=0)
{
for (String j:jure)
{
permissions.add(j);
}
}
}
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
}catch (Exception e)
{
System.out.println("出错了");
}
return null;
}
/**
*
* @param authenticationToken
* @return
* @throws AuthenticationException 进行认证操作
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取用户登录的用户名
String principal = authenticationToken.getPrincipal().toString();
UserInfo user = userInfoService.findUserName(principal);
if(user==null)
{
return null;
}
//获取数据库的盐值
ByteSource bytes = ByteSource.Util.bytes(user.getSalt());
return new SimpleAuthenticationInfo(user,user.getUserpassword(),bytes,getName());
}
}
util层:
package com.example.springboot.util;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.util.ByteSource;
public class Md5Util {
public static String getMd5Util(String pwd, String salt)
{
//加密方式
String method="md5";
//盐值
ByteSource bytes = ByteSource.Util.bytes(salt);
//加密次数
int hashinteration=5;
return new SimpleHash(method,pwd,bytes,hashinteration).toString();
}
}
Controller层:
package com.example.springboot.controller;
import com.example.springboot.service.UserInfoService;
import org.apache.shiro.SecurityUtils;
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 UserInfoController {
@Autowired
UserInfoService userInfoService;
//显示登录页面
@RequestMapping("login_show")
public String login_show()
{
return "Login";
}
//登录提交
@RequestMapping("login_submit")
public String login_submit(String username,String userpassword,Model model)
{
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,userpassword);
try{
subject.login(token);
//这里我就不抛出逐个错误了 这里可以通过抛出的异常来判断用户到底输错了什么
}catch (Exception e){
model.addAttribute("msg","账号密码错误");
return "redirect:login_show";
}
return "redirect:login_success";
}
//登录成功页面
@RequestMapping("login_success")
public String login_success()
{
return "Success";
}
//退出
@RequestMapping("login_out")
public String login_out()
{
Subject subject = SecurityUtils.getSubject();
subject.logout();
//重定向到现实登录页面
return "redirect:login_show";
}
}
resources–>templates–> 两个html网页 一个登录网页 一个登录成功网页
1:login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/login_submit">
<table border="1px solid red" align="center">
<tr>
<td>用户名</td>
<td>
<input type="text" name="username"/>
</td>
</tr>
<tr>
<td>密码</td>
<td>
<input type="text" name="userpassword"/>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="提交"/>
</td>
</tr>
</table>
</form>
</body>
</html>
2:success.html
<!DOCTYPE html>
<html lang="en" xmlns:shiro="http://www.w3.org/1999/xhtml">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--我们在这看看张三有没有emp:add权限-->
<!--这里我们可以看到可以显示添加用户 我们输入一个没有没有这个权限的 -->
<h2>登录成功了 <a href="/login_out">退出当前账户</a>
<!--shiro判断角色权限 用三种可以自行百度细看-->
<a shiro:hasPermission="emp:add">添加用户</a>
<a shiro:hasPermission="emp:del">删除用户</a>
</h2>
</body>
</html>
Mysql数据:
#用户表
create table userinfo
(
userid int primary key auto_increment, --用户主键Id 自增
username varchar(100), --用户名
userpassword varchar(100), --密码
salt varchar(100), --md5加密码的 盐值
hashinteration int --我们通过Md5来加密的次数
)
insert into userinfo()
#角色表
create table roleinfo
(
roleid int primary key auto_increment, --角色主键Id 自增
roleName varchar(100), --角色名称
uid int references userinfo(userid) --用户表外键,因为一个用户可以有多个角色
)
#权限表
create table jurinfo
(
jurId int primary key auto_increment, --主键Id 自增
jurName varchar(100), --权限名称 例如 emp:add
rid int references roleinfo(roleid) --角色的外键
)
--我们在数据库给张三配置几个角色 和权限
--超级管理员 有二个权限
--管理员 有1个权限
--人事经理没有权限
insert into roleinfo(rolename,uid)values('超级管理员',1);
insert into roleinfo(rolename,uid)values('管理员',1);
insert into roleinfo(rolename,uid)values('人事经理',1);
insert into jurinfo(jurname,rid)values('emp:add',1);
insert into jurinfo(jurname,rid)values('emp:update',1);
insert into jurinfo(jurname,rid) values('dept:add',2);
结尾:我是一个新来博客的小白 欢迎大佬来指点错误