我
springboot整合shiro---小白进阶之路
shiro: 授权于认证
----认证,通过一个token来对比用户名与密码是否一致
----授权,比对成功后,授予权限,这里授予的权限是需要与数据库逻辑一致的,shiro中有一个 perms 关键词,拥有某个资源的权限才可以访问
如:perms[admin] 那么登录我的admin用户就可以给他授权,访问哪~
--shiro框架,简单也简单,难也难,反正我个人觉得我应该算入门吧~作为一个新手小白,给想学的新手小白整一个shiro,有什么地方做错了 记得点出来哈
依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<!--thymeleaf依赖-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--shiro-thymeleaf整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--数据库日志数据源mybatis适配包lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency>
<!--shiro依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
第一步:
需要三张数据库,用户表,角色表,用户角色中间表来进行授权于认证
做几个测试数据
User 表(角色表):
perm表(角色):
user_Id_perm
第二步:
1.打开idea新建一个spring项目,项目名字随意
1.1打开application.yml 配置我们 数据库的连接,以及mybatis扫描等...
spring: datasource: url: jdbc:mysql://localhost:3306/shirotest?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 driver-class-name: com.mysql.cj.jdbc.Driver username: root password: root type: com.alibaba.druid.pool.DruidDataSource mybatis: mapper-locations: classpath:mapper/*Mapper.xml type-aliases-package: cn.tedu.shiros.entity server: port: 80
1.2创建实体类
User与Perm,一般是在pojo或者是entity,看个人习惯吧
package cn.tedu.shirotwo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
@Data //生成get和set
@AllArgsConstructor//构造方法
@NoArgsConstructor//无参构造
@Component//给spring进行管理 个人爱好 这段可以省略
public class Perm {
//权限ID
private Integer pid;
//用户名
private String permname;
//权限名
private String detail;
}
package cn.tedu.shirotwo.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
@Data //生成get和set
@AllArgsConstructor//构造方法
@NoArgsConstructor//无参构造
@Component//给spring进行管理 个人爱好 这段可以省略
public class User {
//用户id
private Integer uid;
//用户名
private String username;
//密码
private String password;
//权限信息---这一步是为了我们后面通过用户登录获取权限
private Perm perm;
}
1.3 创建我们的mapper层,mapper是访问数据库的~一般是放在dao层或者是mapper层,这里我就直接创建dao层与mybatis/mapper/*.xml文件了
这里我要给各位科普一下为什么要创建xml文件,我们用mybatis的原因是因为 轻量级,松耦合,持久性等
用传统的jdbc 代码量大且重复,而且不易维护,mybatis呢 xml形式,易操作可扩展功能,当然现在也有一个mybatis-plus,xml都不用写了,言归正传
里面就一个方法,通过我们的用户名去查询数据库是否存在username
package cn.tedu.shirotwo.dao;
import cn.tedu.shirotwo.entity.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
//根据用户名查询用户(登录时校验用户用)
User queryByUsername(String username);
}
<?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="cn.tedu.shirotwo.dao.UserMapper">
<select id="queryByUsername" parameterType="String" resultMap="UserMap" >
SELECT *
FROM
user u,
perm p,
user_Id_perm up
WHERE u.username=#{username}
AND u.uid=up.uid
AND p.pid=up.pid
</select>
<resultMap id="UserMap" type="User">
<id property="uid" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<association property="perm" javaType="Perm">
<id property="pid" column="pid"/>
<result property="permname" column="permname"/>
<result property="detail" column="detail"/>
</association>
</resultMap>
</mapper>
测试下--
OK 有数据--
1.4 编辑我们的service层,这里指的是数据层,如果有需要增强的与 数据库交互 业务在这里编写,我们所谓的entity-dao-service-controller-web-config等 都是为了分而治之,区分每块自己做的工作
package cn.tedu.shirotwo.service;
import cn.tedu.shirotwo.entity.User;
public interface UserService {
//根据用户名查询用户(登录时校验用户用)
User queryByUsername(String username);
}
package cn.tedu.shirotwo.service.impl;
import cn.tedu.shirotwo.dao.UserMapper;
import cn.tedu.shirotwo.entity.User;
import cn.tedu.shirotwo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User queryByUsername(String username) {
return userMapper.queryByUsername(username);
}
}
1.5 开始重点shiro框架编写
package cn.tedu.shirotwo.config;
import cn.tedu.shirotwo.entity.User;
import cn.tedu.shirotwo.service.UserService;
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.springframework.beans.factory.annotation.Autowired;
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("进入授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Subject subject= SecurityUtils.getSubject();//通过这个对象获取能登陆用户
User currentUser = (User) subject.getPrincipal();//获取当前登录对象
info.addStringPermission(currentUser.getPerm().getPermname());//获取权限
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("进入认证");
//获取前端发来的token对象 里面存了用户名和密码
UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;
//将token里的用户名查询数据库是否存在
User user=userService.queryByUsername(token.getUsername());
if(user==null){
return null;//这里是直接返回账户名错误UnknownAccountException 这个对象
}
//这一步是认证密码是否正确
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
package cn.tedu.shirotwo.config;
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.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
/*1.Realm,realm可以理解为一个域,
里面存放了用户的数据,
我们securityManage要验证用户身份,需要从这里获取这是第一步
*/
@Bean
public UserRealm realm(){
return new UserRealm();
}
//2.DefaultWebSecurityManager
@Bean("sec")
public DefaultWebSecurityManager securityManager(@Qualifier("realm")UserRealm realm){
//关联userRealm
DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
SecurityManager.setRealm(realm);
return SecurityManager;
}
//ShiroFilterFactoryBean 过滤器,权限认证在这里操作
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("sec") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);//设置安全管理器
Map<String ,String> map=new HashMap();
/*
anon:无须认证就可以访问
authc:必须认证了才可以访问
user:必须拥有 记住我 才能访问
perms:拥有对某个资源的权限才能访问
role:拥有某项权限才可以访问
*/
map.put("/admin/*", "perms[admin]");
map.put("/test/*", "perms[test]");
map.put("/test/*", "perms[admin]");
map.put("/guest/*", "perms[guest]");
bean.setFilterChainDefinitionMap(map);
//如果权限不够就会发起的请求(路由请求,返回没有权限)
bean.setUnauthorizedUrl("/unauthor");
//如果没有认证就会发送的请求请求(路由请求,让用户登录)
bean.setLoginUrl("/toLogin");
return bean;
}
}
第三步:
编写web,分为三个内容
1:login 登录 index 首页 权限对应的展示页面
index:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">>
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<span th:text="${msg}" style="color: red;"></span>
<a th:href="@{/admin/admin}">admin</a>||<a th:href="@{/test/test}">test</a>||<a th:href="@{/guest/guest}">guest</a>
</body>
</html>
login:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<span th:text="${msg}" style="color: red;"></span>
<form th:action="@{/login}" name="login" method="post">
用户名:<input type="text" name="username">
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
admin:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<p>admin</p>
</body>
</html>
test:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>test</h1>
</body>
</html>
guest:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>guest</h1>
</body>
</html>
这里编写完成了,开始最后的controller:
controller我分为三步
第一:首页与权限不足,以及跳转登录
第二:我的表单提交到login验证
第三:进入admin。test。guest 界面
第一:
package cn.tedu.shirotwo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class UserController {
@RequestMapping({"/","/index","/index.html"})
public String toIndex(Model model){
model.addAttribute("msg", "Hello Shiro");
return "index";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/unauthor")
@ResponseBody
public String toUnauthor(){
return "您的权限不足";
}
}
第二:
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(@RequestParam("username")String username,
@RequestParam("password")String password,
Model model
){
//获取当前用户
Subject subject=SecurityUtils.getSubject();
UsernamePasswordToken token=new UsernamePasswordToken(username,password);
try {
subject.login(token);
return "index";
}catch (UnknownAccountException e){
model.addAttribute("msg", "用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg", "密码错误");
return "login";
}
}
}
第三:
package cn.tedu.shirotwo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class AllurlController {
@RequestMapping("/admin/admin")
public String admin(){
return "/admin/admin";
}
@RequestMapping("/test/test")
public String test(){
return "/test/test";
}
@RequestMapping("/guest/guest")
public String guest(){
return "/guser/guest";
}
}
测试: