环境搭建
- 新建springboot项目,勾选web,thymeleaf
- 编写前端页面
- index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> <hr> <a th:href="@{/user/add}">add</a> <br> <a th:href="@{/user/update}">update</a> </body> </html>
- user/add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>add</h1> </body> </html>
- user/update.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>update</h1> </body> </html>
- 编写对应的controller
@Controller public class MyController { @RequestMapping({"/","/index"}) public String toIndex(){ return "index"; } @RequestMapping("/user/add") public String toAdd(){ return "user/add"; } @RequestMapping("/user/update") public String toUpdate(){ return "user/update"; } }
- 启动项目,页面间正常跳转
登录拦截
UserRealm
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权=>doGetAuthorizationInfo");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证=>doGetAuthorizationInfo");
return null;
}
}
自定义shiro配置
@Configuration
public class ShiroConfig {
/**
* @param defaultWebSecurityManager
* @return ShiroFilterFactoryBean
*/
@Bean
public ShiroFilterFactoryBean factoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
// 设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/*
* 添加过滤器
* anon: 无需认证即可访问
* authc: 认证成功才能访问
* user: 开启记住账号功能才能使用
* */
Map<String, String> map = new LinkedHashMap<>();
// user下的所有url 都需要认证
// map.put("/user/add","authc");
// map.put("/user/update","authc");
map.put("/user/**","authc");
bean.setFilterChainDefinitionMap(map);
// 设置登录页
bean.setLoginUrl("/toLogin");
return bean;
}
/**
* @param userRealm
* @return DefaultWebSecurityManager
*/
@Bean("securityManager")
public DefaultWebSecurityManager securityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
/**
* @return Realm
*/
@Bean("userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
- 数据验证,注入到容器
- 安全管理,set数据(数据验证对象),注入到容器
- 过滤器,set数据(安全管理对象),注入到容器
启动访问,点击add,update 按钮都会被拦截,然后跳转到登录页
用户认证
编写登录页面
login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form th:action="@{/login}" method="post">
username:<input type="text" name="username"><br>
password:<input type="password" name="password"><br>
<input type="submit" value="login">
</form>
</body>
</html>
controller中添加对应的方法
@RequestMapping("/login")
public String login(String username, 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";
}
}
数据验证
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权=>doGetAuthorizationInfo");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了认证=>doGetAuthorizationInfo");
// 模拟数据库中的用户名和密码
String usr = "root";
String pwd = "root";
// 用户名校验
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 如果token中的用户名和数据库中的密码不一样就返回用户名错误
if (!token.getUsername().equals(usr)) {
// 抛出异常 UnknownAccountException
return null;
}
// 密码校验 传递给shiro进行校验 不用自己校验
return new SimpleAuthenticationInfo("", pwd, "");
}
}
启动访问,点击add,update 按钮都会被拦截,然后跳转到登录页,输入 root,root 才能登录成功
shiro整合mybatis
-
导入依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.20</version> </dependency> <!-- https://mvnrepository.com/artifact/log4j/log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.22</version> </dependency> <!--引入mybatis,这是Mybatis官方提供的适配SpringBoot的,而不是SpringBoot自己的--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.1</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
-
编写实体类
User
@Data @AllArgsConstructor @NoArgsConstructor public class User { private Integer id; private String name; private String pwd; }
-
编写mapper
UserMapper
@Repository @Mapper public interface UserMapper { User queryUserByName(String name); }
-
编写mapper.xml
UserMapper.xml
<?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.starry.mapper.UserMapper"> <select id="queryUserByName" resultType="user"> select * from mybatis.user where name = #{name}; </select> </mapper>
-
编写service
UserService
public interface UserService { User queryUserByName(String name); }
UserServiceImpl
@Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; @Override public User queryUserByName(String name) { return userMapper.queryUserByName(name); } }
-
添加配置
properties.yml
spring: datasource: username: root password: root #?serverTimezone=UTC解决时区的报错 url: jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource #Spring Boot 默认是不注入这些属性值的,需要自己绑定 #druid 数据源专有配置 initialSize: 5 minIdle: 5 maxActive: 20 maxWait: 60000 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false poolPreparedStatements: true #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入 #如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j filters: stat,wall,log4j maxPoolPreparedStatementPerConnectionSize: 20 useGlobalDataSourceStat: true connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
application.properties
#指定Mybatis的Mapper文件 mybatis.mapper-locations=classpath:mapper/*.xml #指定Mybatis的实体目录 起别名 mybatis.type-aliases-package=com.starry.pojo
-
修改方法
UserRealm的 认证 方法protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了认证=>doGetAuthorizationInfo"); // 模拟数据库中的用户名和密码 // String usr = "root"; // String pwd = "root"; UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // 用户名校验 User user = userService.queryUserByName(token.getUsername()); if (user == null) { return null; } // 如果token中的用户名和数据库中的密码不一样就返回用户名错误 /*if (!token.getUsername().equals(usr)) { // 抛出异常 UnknownAccountException return null; }*/ // 密码校验 传递给shiro进行校验 不用自己校验 // return new SimpleAuthenticationInfo("", pwd, ""); return new SimpleAuthenticationInfo("", user.getPwd(), ""); }
-
启动服务,数据库中的用户都能登录
请求授权
从数据库中获取权限
修改数据库添加权限字段
修改实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pwd;
private String perms;
}
shiro过滤方法 添加过滤规则
// 授权 拥有user:add权限的用户才能访问 /user/add 资源
map.put("/user/add", "perms[user:add]");
map.put("/user/update", "perms[user:update]");
// 设置没有权限跳转的url
bean.setUnauthorizedUrl("/unauth");
修改 认证 方法
// 传入principal对象为 user 授权方法即可使用user
return new SimpleAuthenticationInfo(user, user.getPwd(), "");
编写授权方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权=>doGetAuthorizationInfo");
// 获取当前的登录对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
// 如果权限不是null就赋予对应的权限
if (currentUser.getPerms()!=null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 赋予权限
info.addStringPermission(currentUser.getPerms());
return info;
}
return null;
}
编写controller
@RequestMapping("/unauth")
@ResponseBody
public String unauthorized(){
return "未授权,无法访问";
}
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "/login";
}
shiro整合thymeleaf
导入依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
ShiroConfig
/**
* @return shiro-thymeleaf方言
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<!-- 如果未认证就显示登录 -->
<div shiro:notAuthenticated="">
<a th:href="@{/toLogin}">登录</a>
</div>
<!-- 如果已经认证就显示退出 -->
<div shiro:authenticated="">
<a th:href="@{/logout}">退出</a>
</div>
<hr>
<!-- 如果拥有权限 user:add 就显示 -->
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<br>
<!-- 如果拥有权限 user:update 就显示 -->
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
启动服务,点击 登录
- 输入 starry,starry ,只显示 add 按钮
- 输入 root,root ,只显示 update 按钮
- 输入 guest,guest ,不显示按钮
项目结构
项目下载: 点击下载