9、Shiro
1、导入依赖
<dependencies>
<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>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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--前端交互整合-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- shiro-spring -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
2、application.properties
# 应用名称
spring.application.name=springboot-08-shiro
# 应用服务 WEB 访问端口
server.port=8080
# 数据库驱动:
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源名称
spring.datasource.name=defaultDataSource
# 数据库连接地址
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC
# 数据库用户名&密码:
spring.datasource.username=root
spring.datasource.password=root
# THYMELEAF (ThymeleafAutoConfiguration)
# 开启模板缓存(默认值: true )
spring.thymeleaf.cache=false
#整合mybatis
mybatis.type-aliases-package=com.hui.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
3、后端代码
- Controller
@Controller
public class ShiroController {
@RequestMapping({"/", "/index"})
public String toIndex(Model model) {
model.addAttribute("msg", "首页");
return "index";
}
@RequestMapping("/user/add")
public String addUser() {
return "user/add";
}
@RequestMapping("/user/update")
public String updateUser() {
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, Model model) {
// 获取用户
Subject user = SecurityUtils.getSubject();
// 封装参数,获取token
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
// 验证登录
try {
// 执行登录操作,跨类调用
user.login(token);
model.addAttribute("msg", "成功登录");
return "index";
} catch (UnknownAccountException uae) {
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException ice) {
model.addAttribute("msg", "用户密码错误");
return "login";
}
}
@RequestMapping("/noAuth")
@ResponseBody
public String noAuth() {
return "未授权,无法访问";
}
}
-
ShiroConfig(shiro配置)
@Configuration public class ShiroConfig { // 3 获取ShiroBean @Bean public ShiroFilterFactoryBean getShiroFilterFactoryBean( @Qualifier("getDefaultWebSecurityManager") WebSecurityManager securityManager ) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); // 设置安全管理器 bean.setSecurityManager(securityManager); // 添加shiro的内置过滤器 /* anon: 无需认证就可以登录 authc:必须认证才能登录 user: 必须拥有“记住我”这个功能 perms:拥有对某个资源的权限才能访问 role:拥有某个角色才能访问 */ LinkedHashMap<String, String> map = new LinkedHashMap<>(); // 权限授权,访问url需要权限,支持通配符 map.put("/user/add", "perms[user:add]"); map.put("/user/update", "perms[user:update]"); bean.setFilterChainDefinitionMap(map); // 设置登录url映射 bean.setLoginUrl("/toLogin"); // 设置未授权的请求 bean.setUnauthorizedUrl("/noAuth"); return bean; } // 2 获取安全管理器 @Bean(name = "getDefaultWebSecurityManager") public DefaultWebSecurityManager getDefaultWebSecurityManager( @Qualifier("userRealm") UserRealm userRealm) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 直接userRealm()传参也可以,这里演示Spring指定自动注入 securityManager.setRealm(userRealm); return securityManager; } // 1 创建realm对象,需要自定义另一个类 @Bean(name = "userRealm") public UserRealm userRealm() { return new UserRealm(); } }
-
UserRealm(认证授权)
public class UserRealm extends AuthorizingRealm { // Subject 用户 // SecurityManager 管理所有用户 // Realm 连接数据 @Autowired private UserServiceImpl userService; // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 进入被拦截的url,就会进这个info SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); // 授权应该从数据库查出权限字段 // info.addStringPermission("user:add"); // 从 new SimpleAuthenticationInfo(queryUser, queryUser.getPwd(), "");传递过来第一个参数user最为subject Subject subject = SecurityUtils.getSubject(); User currentUser = (User) subject.getPrincipal(); // 从数据库中获取验证权限 simpleAuthorizationInfo.addStringPermission(currentUser.getPerms()); return simpleAuthorizationInfo; } // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // 模拟数据库中查出用户名、密码 UsernamePasswordToken userToken = (UsernamePasswordToken) token; User user = userService.queryUserByName(userToken.getUsername()); // 验证用户名 if (user == null) { // 用户名不正确,就抛出UnknownAccountException return null; } Subject currentSubject = SecurityUtils.getSubject(); Session session = currentSubject.getSession(); session.setAttribute("loginUser", user); // 密码验证,shiro完成,不需要用户判断.直接返回 return new SimpleAuthenticationInfo(user, user.getPwd(), ""); } }
-
UserMapper(mapper)
@Repository @Mapper public interface UserMapper { User queryUserByName(String name); }
-
UserMapper.xml(整合mybatis)
<?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.hui.mapper.UserMapper"> <select id="queryUserByName" parameterType="String" resultType="User"> select * from mybatis.user where name = #{name} </select> </mapper>
-
User(pojo)
@AllArgsConstructor @Data @NoArgsConstructor public class User { private int id; private String name; private String pwd; private String perms; }
-
UserService(Service)
public interface UserService { User queryUserByName(String name); }
-
UserServiceImpl(Service)
@Service public class UserServiceImpl implements UserService { @Autowired UserMapper userMapper; public UserServiceImpl(UserMapper userMapper) { this.userMapper = userMapper; } @Override public User queryUserByName(String name) { return userMapper.queryUserByName(name); } }
4、前端页面
-
index.html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro" xmlns:shiro="http://www.pollix.at/thymeleaf/shiro"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> <div th:text="${msg}"></div> <!--/*@thymesVar id="loginUser" type=""*/--> <!--<div th:if="${session.loginUser ==null}">--> <!--<a th:href="@{/toLogin}">登录</a>--> <!--</div>--> <div shiro:hasPermission="add"> <a th:href="@{/user/add}">add</a> </div> <div shiro:hasPermission="update"> <a th:href="@{/user/update}">update</a> </div> </body> </html>
-
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}"> 用户名:<input type="text" name="username"><br> 密码:<input type="password" name="password"> <br> <input type="submit" name="提交"> </form> </body> </html>
-
add.html(user包)
<h1>add</h1>
-
update.html(user包)
<h1>update</h1>