创建项目
1、开发数据库注册
1、开发注册界面
<h1>用户注册</h1>
<form action="/user/register" method="post">
用户名:<input type="text" name="username" > <br/>
密码 : <input type="text" name="password"> <br>
<input type="submit" value="立即注册">
</form>
2、创建数据表结构
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL,
`password` varchar(40) DEFAULT NULL,
`salt` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
3、项目引入依赖
<!--mybatis相关依赖-->
<!--mybatis相关依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3.4</version>
</dependency>
<!--引入shiro整合springboot依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
<scope>runtime</scope>
</dependency>
4、配置application.yaml配置文件
spring:
application:
# 应用名字
name: shiro
mvc:
view:
prefix: /
suffix: .html
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/shirodemo?characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
server:
# 端口号
port: 8888
mybatis:
#别名
type-aliases-package: com.zhubayi.shirodb.entity
#xml的位置
mapper-locations: classpath:mapper/*Mapper.xml
5、创建entity
/**
* @author zhubayi
*/
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private String id;
private String username;
private String password;
private String salt;
}
6、创建Mapper接口
/**
* @author zhubayi
*/
@Mapper
public interface UserMapper extends BaseMapper<User> {
void save(User user);
}
7、开发mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhubayi.shirodb.mapper.UserMapper">
<insert id="save" parameterType="com.zhubayi.shirodb.entity.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(#{id},#{username},#{password},#{salt})
</insert>
</mapper>
8、开发service接口
/**
* @author zhubayi
*/
/**
* @author zhubayi
*/
public interface UserService extends IService<User> {
/**
* 注册
* @param user
*/
void register(User user);
}
8、创建salt工具类
/**
* @author zhubayi
*/
public class SaltUtils {
/**
* 生成salt的静态方法
* @param n
* @return
*/
public static String getSalt(int n){
char[] chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567890!@#$%^&*()".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
char aChar = chars[new Random().nextInt(chars.length)];
sb.append(aChar);
}
return sb.toString();
}
}
9、开发service实现类
/**
* @author zhubayi
*/
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 注册
*
* @param user
*/
@Override
public void register(User user) {
//1.生产随机盐
String salt = SaltUtils.getSalt(8);
//2.把随机盐保存到数据库
user.setSalt(salt);
//3.明文密码进行md5 + salt + hash散列
Md5Hash md5Hash = new Md5Hash(user.getPassword(),salt,1024);
user.setPassword(md5Hash.toHex());
userMapper.save(user);
}
}
10、开发Controller
/**
* @author zhubayi
*/
@Controller
@RequestMapping("user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("register")
public String register(User user){
System.out.println(user);
try {
userService.register(user);
return "redirect:/login.html";
}catch (Exception e){
e.printStackTrace();
return "redirect:/register.html";
}
}
}
11、启动项目进行注册
2、开发数据库认证
1、开发Mapper
//根据身份信息认证的方法
User findByUserName(@Param("username") String username);
2、开发mapper配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhubayi.shirodb.mapper.UserMapper">
<insert id="save" parameterType="com.zhubayi.shirodb.entity.User" useGeneratedKeys="true" keyProperty="id">
insert into t_user values(#{id},#{username},#{password},#{salt})
</insert>
<select id="findByUserName" parameterType="String" resultType="com.zhubayi.shirodb.entity.User">
select id,username,password,salt from t_user
where username = #{username}
</select>
</mapper>
3、开发Service接口
/**
* @author zhubayi
*/
public interface UserService extends IService<User> {
/**
* 注册
* @param user
*/
void register(User user);
User findByUserName(String username);
}
4、开发Service实现类
@Override
public User findByUserName(String username) {
return userMapper.findByUserName(username);
}
5、发在工厂中获取bean对象的工具类
/**
* @author zhubayi
*/
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
//根据bean名字获取工厂中指定bean 对象
public static Object getBean(String beanName){
return context.getBean(beanName);
}
}
6、修改自定义realm
/**
* @author zhubayi
*/
public class MyRealm extends AuthorizingRealm {
private UserService userService;
public MyRealm(UserService userService){
this.userService=userService;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
/**
* 认证
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//获取身份信息
String username=(String) authenticationToken.getPrincipal();
//得到service
//得到用户信息
User user=userService.findByUserName(username);
if(!ObjectUtils.isEmpty(user)){
//返回数据库信息
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),this.getName());
}
return null;
}
}
7、修改ShiroConfig中realm使用凭证匹配器以及hash散列
@Bean
public Realm getRealm(UserService userService){
MyRealm myRealm = new MyRealm(userService);
//设置hash的凭证匹配器
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//设置md5加密
credentialsMatcher.setHashAlgorithmName("md5");
//设置散列次数
credentialsMatcher.setHashIterations(1024);
myRealm.setCredentialsMatcher(credentialsMatcher);
return myRealm;
}
8、开发controller并且测试
@PostMapping("login")
public String login(String username,String password){
//获取主体对象
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username,password));
return "redirect:/index.html";
}catch (UnknownAccountException e){
e.printStackTrace();
System.out.println("用户名错误");
}catch (IncorrectCredentialsException e){
e.printStackTrace();
System.out.println("密码错误");
}
return "redirect:/login.html";
}
登录成功~
3、授权实现
1、添加依赖
<!-- ThymeLeaf 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.1.0</version>
</dependency>
2、页面资源授权
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1><shiro:principal/>欢迎进入后台管理系统</h1>
<h2><a href="/user/logout">退出登录</a></h2>
<li shiro:hasAnyRoles="user,admin"><a href="">用户管理</a>
<ul>
<li shiro:hasPermission="user:add:*"><a href="">添加</a></li>
<li shiro:hasPermission="user:delete:*"><a href="">删除</a></li>
<li shiro:hasPermission="user:update:*"><a href="">修改</a></li>
<li shiro:hasPermission="user:find:*"><a href="">查询</a></li>
</ul>
</li>
</div>
<div shiro:hasRole="admin">
<li><a href="">商品管理</a></li>
<li><a href="">订单管理</a></li>
<li><a href="">物流管理</a></li>
</div>
</body>
</html>
标签说明:
-
shiro:principal // 当前用户的登录信息,用户名之类
-
shiro:guest="" // 验证是否是游客,即未认证的用户
-
shiro:user="" // 验证是否是已认证或已记住用户
-
shiro:authenticated="" // 验证是否是已认证用户,不包括已记住用户
-
shiro:notAuthenticated= “” // 未认证用户,但是 已记住用户
-
shiro:lacksRole=“admin” // 表示没有 admin 角色的用户
-
shiro:hasAllRoles=“admin, user1” // 表示需要同时拥有两种角色
-
shiro:hasAnyRoles=“admin, user1” // 表示 拥有其中一个角色即可
-
shiro:lacksPermission=“admin:delete” // 类似于 shiro:lacksRole
-
shiro:hasAllPermissions=“admin:delete, admin:edit” // 类似于 shiro:hasAllRoles
-
shiro:hasAnyPermission=“admin:delete, admin:edit” // 类似于 hasAnyRoles
加入shiro的方言配置
- 页面标签不起作用一定要记住加入方言处理
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
3、代码方式授权
@RequestMapping("save")
public String save(){
System.out.println("进入方法");
//获取主体对象
Subject subject = SecurityUtils.getSubject();
//代码方式
if (subject.hasRole("admin")) {
System.out.println("保存订单!");
}else{
System.out.println("无权访问!");
}
//基于权限字符串
//....
return "redirect:/index.jsp";
}
4、方法调用授权
- @RequiresRoles 用来基于角色进行授权
- @RequiresPermissions 用来基于权限进行授权
@RequiresRoles(value={"admin","user"})//用来判断角色 同时具有 admin user
@RequiresPermissions("user:update:01") //用来判断权限字符串
@RequestMapping("save")
public String save(){
System.out.println("进入方法");
return "redirect:/index.jsp";
}
5、授权数据持久化
一共需要五张表。
sql语句:
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for t_pers
-- ----------------------------
DROP TABLE IF EXISTS `t_perms`;
CREATE TABLE `t_pers` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`name` varchar(80) DEFAULT NULL,
`url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`name` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for t_role_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_role_perms`;
CREATE TABLE `t_role_perms` (
`id` int(6) NOT NULL,
`roleid` int(6) DEFAULT NULL,
`permsid` int(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL,
`password` varchar(40) DEFAULT NULL,
`salt` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
`id` int(6) NOT NULL,
`userid` int(6) DEFAULT NULL,
`roleid` int(6) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
SET FOREIGN_KEY_CHECKS = 1;
然后根据数据表创建实体类
Perms:
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Perms implements Serializable {
@TableId(type = IdType.AUTO)
private String id;
private String name;
private String url;
}
User:
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private String id;
private String username;
private String password;
private String salt;
@TableField(exist = false)
private List<Role> roles;
}
Role:
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class Role implements Serializable {
@TableId(type = IdType.AUTO)
private String id;
private String name;
//定义权限的集合
@TableField(exist = false)
private List<Perms> perms;
}
6、创建Mapper方法
//根据用户名查询所有角色
User findRolesByUserName(String username);
//根据角色id查询权限集合
List<Perms> findPermsByRoleId(@Param("id") String id);
7、mapper实现
<resultMap id="userMap" type="com.zhubayi.shirodb.entity.User">
<id column="uid" property="id"/>
<result column="username" property="username"/>
<!--角色信息-->
<collection property="roles" javaType="list" ofType="com.zhubayi.shirodb.entity.Role">
<id column="id" property="id"/>
<result column="rname" property="name"/>
</collection>
</resultMap>
<select id="findRolesByUserName" parameterType="String" resultMap="userMap">
SELECT u.id uid,u.username,r.id,r.NAME rname
FROM t_user u
LEFT JOIN t_user_role ur
ON u.id=ur.userid
LEFT JOIN t_role r
ON ur.roleid=r.id
WHERE u.username=#{username}
</select>
<select id="findPermsByRoleId" parameterType="String" resultType="com.zhubayi.shirodb.entity.Perms">
SELECT p.id,p.NAME,p.url,r.NAME
FROM t_role r
LEFT JOIN t_role_perms rp
ON r.id=rp.roleid
LEFT JOIN t_perms p ON rp.permsid=p.id
WHERE r.id=#{id}
</select>
8、Service接口
//根据用户名查询所有角色
User findRolesByUserName(String username);
//根据角色id查询权限集合
List<Perms> findPermsByRoleId(String id);
8、Service实现
@Override
public User findRolesByUserName(String username) {
return userMapper.findRolesByUserName(username);
}
@Override
public List<Perms> findPermsByRoleId(String id) {
return userMapper.findPermsByRoleId(id);
}
9、修改自定义realm
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到主体
String primaryPrincipal =(String) principalCollection.getPrimaryPrincipal();
//得到用户信息包含权限权限信息
User user = userService.findRolesByUserName(primaryPrincipal);
//授权信息
if(!CollectionUtils.isEmpty(user.getRoles())){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
user.getRoles().forEach(role -> {
//角色信息
simpleAuthorizationInfo.addRole(role.getName());
//权限信息
List<Perms> perms = userService.findPermsByRoleId(role.getId());
// 功能列表
Set<String> menus = new HashSet();
if(!CollectionUtils.isEmpty(perms)){
for (Perms permission : perms) {
if(StringUtils.isNotEmpty(permission.getName())) { //加一个非空判断
menus.add(permission.getName());
}
}
simpleAuthorizationInfo.addStringPermissions(menus);
}
});
return simpleAuthorizationInfo;
}
return null;
}
10、启动测试
在数据库添加数据:
用户数据:t_user
角色数据:t_role
*
用户角色关联数据:t_user_role
菜单权限数据:t_perms
角色菜单权限关联数据:t_role_perms
这个用户是普通用户登录之后只能看到,用户管理。
当再t_user_role
表增加一个管理管理员角色。
刷新之后,就可以看到管理员所拥有的权限。