SpringBoot扩展——Shiro
一、简介
- Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
与SpringSecurity的区别
- shiro:灵活性强,易学易扩展。同时,不仅可以在web中使用,可以工作在任务环境内中。
- security:灵活性较差,但与spring整合性好。
- 应用环境:不依赖于任何框架,可在JavaSE中使用,更常见的是JavaEE中。
- 功能:管理应用程序中与安全相关的全部,同时尽可能支持多种实现方法。Shiro是建立在完善的接口驱动设计和面向对象原则之上的,支持各种自定义行为。
二、功能
Shiro不会去维护用户、维护权限,需要自己设计,通过相应的接口注入给Shiro。
四大基石----身份验证,授权,会话管理,加密
- Authentication:用户认证,验证用户是不是拥有相应的身份。
- Authorization:用户授权,验证某个已认证的用户是否拥有某个权限;即判断用户是否能做事情。
- Session Manager:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中。
- Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储。
Shiro扩展功能
-
Web Support:Web支持,可以非常容易的集成到Web环境。
-
Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率。
-
Concurrency:shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去。
-
Testing:提供测试支持。
-
Run As:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问。
-
Remember Me:记住我。
Shiro尝试在任何应用环境下实现这些功能,而不依赖其他框架、容器或应用服务器。
三、架构
Shiro 有三大核心组件,即 Subject、SecurityManager 和 Realm。
Subject——主体
- 外部应用于subject进行交互,subject记录了当前的操作方————记为主体。可以是用户或者网络爬虫。
- 作用:主体需要访问系统,系统则对其进行认证和授权
- 结构:包含Principals 和 Credentials 两个信息
Principals:代表身份。可 以是用户名、邮件、手机号码等等,用来标识一个登录主体的身份。
Credentials:代表凭证。常见的有密码,数字证书等等。
外部程序通过subject(用户)进行认证授权,而subject是通过SecurityManager安全管理器进行认证授权,即:
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager——安全管理器
- Shiro框架的核心。典型的Facade模式
- 作用:Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务(认证和授权)。
- 结构:SecurityManager是一个接口,继承了Authenticator, Authorizer, SessionManager三个接口。
原理:SecurityManager是通过Authenticator进行认证,通过Authorizer进行授权,通过SessionManager进行会话管理等。
开发人员将大部分精力放在了 Subject 认证主体上,与 Subject 交互背后的安全操作,则由 SecurityManager 来完成。
Authenricator——认证器
- 作用:对Subject进行认证。Subject的信息在shrio中是通过AuthenticationToken对象来储存,由AuthenricationStrategy进行验证管理。
- Authenticator是一个接口,通过ModularRealmAuthenticator实现类基本上可以满足大多数需求,也可以自定义认证器。
Authorizer——授权器
- 作用:Subject认证后,授权器来对其授予对应角色权限。即控制着用户能访问应用中的哪些功能。
SessionManager——会话管理器
- 作用:Shiro框架定义了一套会话管理,它不依赖web容器的session
- 优点:Shiro可以使用在非web应用上,也可以将分布式应用的会话集中在一点管理,此特性可使它实现单点登录。
可以理解为:Shiro抽象了一个自己的Session来管理主体与应用之间交互的数据。
SessionDao——会话DAO
- Session的接口,Shiro通过它来管理session数据。
- 作用:个性化的session数据储存需要使用sessionDao。
CacheManager——缓存控制器
- 作用:主要对Session数据和用户权限数据进行缓存,减小数据库的访问压力。
Realm——领域
- 相当于DataSource数据源。
- 作用:securityManager进行安全认证需要通过Realm获取用户权限数据。比如:如果用户身份数据在数据库那么realm就需要从数据库获取用户身份信息。
- 原理:需继承AuthorizingRealm接口。至少有一个——用于认证和授权,也可自定义多个。
Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。
- 例如:当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
Shiro不知道用户或权限存储在哪及以何种格式存储,所以一般在应用中都需要实现自己的Realm密码模块
注意:Subject进行认证和授权都需要调用realm,所以realm不仅仅相当于数据源,还包含了认证和授权的一种逻辑。
- 实质:Realm是一个安全相关的DAO,封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。
Cryptography——密码管理器
- 作用:提供了一套加密/解密的组件。比如常用的散列,加/解密等功能。
日常练习所使用的md5算法其实是一种散列算法,只能加密,不能解密。
总结:Shrio的核心部分还是认证和授权部分,其他都是围绕这俩部分进行的。
四、认证流程
- 应用程序代码调用 Subject.login(token) 方法,传入代表最终用户身份的 AuthenticationToken 实例 Token。
- 将 Subject 实例委托给应用程序的 SecurityManager,开始真正的认证工作。
- SecurityManager 根据注入的 Realm ,让SecurityManager得到合法的用户和权限进行判断和安全认证。(Realm可自定义)
五、实现步骤
案例一:搭建环境,进行测试
- 创建SpringBoot项目,添加web框架,导入thymeleaf、Shiro依赖。
<!--thymeleaf-->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
<!--Shiro-Spring-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</version>
</dependency>
- 在templates下创建index首页,编写controller,进行测试。
@Controller
public class testController {
@RequestMapping("/index")
public String toIndex(Model model){
model.addAttribute("msg","这是一个Shiro的测试案例");
return "index";
}
}
- config包下自定义UserRealm
package com.zy.Config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
public class UserRealm extends AuthorizingRealm {
/*用户授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用了=========用户授权方法");
return null;
}
/*用户认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用了=========用户认证方法");
return null;
}
}
- 编写ShrioConfiguration配置类
作用:装配ShiroFilterFactoryBean,DefaultWebSecurityManager和自定义的UserRealm
package com.zy.Config;
import org.apache.shiro.mgt.DefaultSecurityManager;
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;
@Configuration
public class ShrioConfiguration {
// 装配ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
return shiroFilterFactoryBean;
}
// 装配SecurityManager,注入自定义的UserRealm
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 关联自定义的UserRealm
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
// 装配Realm
@Bean(name = "userRealm")
public UserRealm getUserRealm(){
return new UserRealm();
}
}
- 配置controller
@Controller
public class testController {
@RequestMapping("/user/add")
public String add(){
return "/user/add";
}
@RequestMapping("/user/update")
public String update(){
return "/user/update";
}
@RequestMapping("/user/show")
public String show(){
return "/user/show";
}
}
- 编写前端网页,进行测试
index.html
<body th:align="center">
<h1>首页</h1>
<h2 th:text="${msg}"></h2>
<hr>
<a th:href="@{/user/show}">展示页面</a> |
<a th:href="@{/user/add}">添加页面</a> |
<a th:href="@{/user/update}">修改页面</a>
</body>
add.html
<body align="center">
<h2>add页面</h2>
</body>
update.html
<body align="center">
<h2>update页面</h2>
</body>
show.html
<body align="center">
<h2>show页面</h2>
</body>
总结:三个网页均可进入。
案例二:添加登录拦截权限
- 在配置类的ShiroFilterFactoryBean中添加权限拦截功能
拦截的权限有:
- anon:无需认证就可以访问
- authc:必须认证了才能访问
- user:必须拥有记住我功能才能使用
- perms:拥有对某个资源的权限才能访问
- role:拥有某个角色权限才能访问
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加Shiro内置过滤器
HashMap<String, String> map = new HashMap<>();
map.put("/user/show","anon"); // 不用认证
map.put("/user/add","authc");
map.put("/user/update","authc");
// 设置拦截权限
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 拦截后跳转到登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
- 自定义登录页面,配置controller
login.html
<body th:align="center">
<h1>登录</h1>
<hr>
<form method="post" action="/login">
用户名:<input type="text" name="username">
密码:<input type="password" name="pwd">
<input type="submit" value="提交">
</form>
</body>
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
总结:show网页不用登录即可查看,add、update网页会进入登录网页。
案例三:实现用户认证功能
用户认证步骤:
- 获取当前用户
- 封装用户登录信息生成Token
- 执行登录操作,可自定义捕获异常
- controller中配置login
@RequestMapping("/login")
public String login(String username,String pwd,Model model){
// 1. 获取当前用户
Subject subject = SecurityUtils.getSubject();
// 2. 封装用户信息
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
try{
// 3. 执行登录操作 成功后返回首页
subject.login(token);
return "index";
}catch (UnknownAccountException e){
// 捕获异常
model.addAttribute("msg","登陆失败!用户名不存在!!");
return "login";
}catch (IncorrectCredentialsException e){
// 捕获异常
model.addAttribute("msg","登陆失败!密码错误!!");
return "login";
}
}
- login.html中添加msg显示
<p th:text="${msg}" style="color: red"></p>
- UserRealm中编写认证检查逻辑
/*用户认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用了=========用户认证方法");
// 自定义用户 也可从数据库中查询
String username = "root";
String pwd = "123";
// 获取Token————controller中使用后,全局可用
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 判断用户名
if(!username.equals(token.getUsername())){
// 若不一致,自动抛出异常
return null;
}
// Shiro帮我们判断密码
return new SimpleAuthenticationInfo("",pwd,"");
}
总结:获取表单中的用户信息,与设定好的信息进行比对,一致即可认证成功,成功登陆。、
仅用比对用户名,密码Shiro会帮我们自动比对。
案例四:整合MyBatis,连接数据库
- 导入mysql连接、Druid、MyBatis-Spring等jar包
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--Log4J-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--MyBatis-Spring-starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
- 编写application.yaml和application.properties配置文件
application.properties
mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
application.yaml
spring:
datasource:
username: root
password: zy123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
- 编写实体类pojo
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
}
- 编写Dao层UserMapper,UserMapper.xml
@Repository
@Mapper
public interface UserMapper {
User getUserByName(@Param("name") String name);
}
- 编写service层 UserService,UserServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByName(String name) {
return userMapper.getUserByName(name);
}
}
- UserRealm中查询数据库获取用户
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用了=========用户认证方法");
// 1. 获取Token————controller中使用后,全局可用
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 2. 数据库中查询是否存在
User user = userService.getUserByName(token.getUsername());
if (user!=null){
// 3. Shiro帮我们判断密码
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
// 用户不存在,自动抛出异常
return null;
}
总结:从数据库中查询用户数据,与登录的数据进行比对,一致即可认证成功。
案例五:实现用户权限功能
数据库中user表添加perms字段,用于设置权限
- ShrioConfiguration中 更改权限设置
HashMap<String, String> map = new HashMap<>();
map.put("/user/update","perms[user:update]");// perms值为user:update 可进入更新页面
map.put("/user/add","perms[user:add]");// perms值为user:add 可进入添加页面
map.put("/user/show","authc"); // 登录查看
// 设置拦截权限
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 拦截后跳转到登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 没有权限后显示 用户被拦截
shiroFilterFactoryBean.setUnauthorizedUrl("/error401");
return shiroFilterFactoryBean;
- 设置未授权响应字符串即controller访问路径
@RequestMapping("/error401")
@ResponseBody
public String error401(){
return "未经授权,无法访问该页面!";
}
- UserRealm中设置用户授权操作
/*用户授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用了=========用户授权方法");
// info:用户授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
// 获取用户perms属性的值,用于设置权限
info.addStringPermission(user.getPerms());
return info;
}
结果:数据库中添加perms字段,用于设置用户权限,拥有权限的用户才可访问,否则进入/error401,显示没有权限的信息。
案例六:实现用户注销功能
- controller中设置路径
@RequestMapping("/logout")
public String logout(HttpSession session){
// 1. 获取当前用户
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/toLogin";
}
- ShrioConfiguration的map中添加权限
map.put("/logout","logout");
// 设置拦截权限
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
- index.html中添加注销链接
<a th:href="@{/logout}">注销</a>
总结:用户可注销。
案例七:实现部分显示功能
- Shiro整合thymeleaf,导入对应jar包
<!-- Shiro整合thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
- 在ShrioConfiguration中装配thymeleaf
// 装配shiro整合thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
- 修改index.html
注意:导入Shiro整合thymeleaf命名空间
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
要求:对用权限的用户显示对应信息,未登录显示登录,已登录显示注销
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body th:align="center">
<h1>首页</h1>
<!--未登录显示登录-->
<shiro:notAuthenticated>
<a th:href="@{/toLogin}">登录</a>
</shiro:notAuthenticated>
<!--登陆之后显示注销-->
<shiro:authenticated>
<span th:text="${username}">用户名:</span>
<a th:href="@{/logout}">注销</a>
</shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示页面</a>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">添加页面</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">修改页面</a>
</div>
</body>
</html>
六、整体项目
项目结构
- Pom.xml依赖
<dependencies>
<!-- Shiro整合thymeleaf-->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<!--数据库连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--Log4J-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!--Druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<!--MyBatis-Spring-starter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
<!--Shiro-Spring-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.6.0</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>
<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>
</dependencies>
- ShrioConfiguration配置文件
@Configuration
public class ShrioConfiguration {
// 装配ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager ){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加Shiro内置过滤器
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有记住我功能才能使用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
HashMap<String, String> map = new HashMap<>();
map.put("/user/update","perms[user:update]");// perms值为user:update 可进入更新页面
map.put("/user/add","perms[user:add]");// perms值为user:add 可进入添加页面
map.put("/user/show","anon"); // 直接查看
map.put("/logout","logout");
// 设置拦截权限
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
// 拦截后跳转到登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 没有权限后显示 用户被拦截
shiroFilterFactoryBean.setUnauthorizedUrl("/error401");
return shiroFilterFactoryBean;
}
// 装配SecurityManager,注入自定义的UserRealm
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 关联自定义的UserRealm
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
// 装配Realm
@Bean(name = "userRealm")
public UserRealm getUserRealm(){
return new UserRealm();
}
// 装配shiro整合thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
- 自定义UserRealm
public class UserRealm extends AuthorizingRealm {
@Autowired
UserServiceImpl userService;
/*用户认证*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("调用了=========用户认证方法");
// 1. 获取Token————controller中使用后,全局可用
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 2. 数据库中查询是否存在
User user = userService.getUserByName(token.getUsername());
if (user!=null){
// 3. Shiro帮我们判断密码
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
// 用户不存在,自动抛出异常
return null;
}
/*用户授权*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("调用了=========用户授权方法");
// info:用户授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 获取当前用户
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
// 获取用户perms属性的值,用于设置权限
info.addStringPermission(user.getPerms());
return info;
}
}
- testController控制跳转
@Controller
public class testController {
@RequestMapping("/index")
public String toIndex(Model model){
return "index";
}
@RequestMapping("/user/add")
public String add(){
return "/user/add";
}
@RequestMapping("/user/update")
public String update(){
return "/user/update";
}
@RequestMapping("/user/show")
public String show(){
return "/user/show";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
@RequestMapping("/logout")
public String logout(HttpSession session){
// 1. 获取当前用户
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "redirect:/toLogin";
}
@RequestMapping("/login")
public String login(String username,String pwd,Model model){
// 1. 获取当前用户
Subject subject = SecurityUtils.getSubject();
// 2. 封装用户信息
UsernamePasswordToken token = new UsernamePasswordToken(username, pwd);
model.addAttribute("username",token.getUsername());
try{
// 3. 执行登录操作 成功后返回首页
subject.login(token);
return "index";
}catch (UnknownAccountException e){
// 捕获异常
model.addAttribute("msg","登陆失败!用户名不存在!!");
return "login";
}catch (IncorrectCredentialsException e){
// 捕获异常
model.addAttribute("msg","登陆失败!密码错误!!");
return "login";
}
}
@RequestMapping("/error401")
@ResponseBody
public String error401(){
return "未经授权,无法访问该页面!";
}
}
- 数据库
User实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
private String perms;
}
UserMapperDoa层
@Repository
@Mapper
public interface UserMapper {
User getUserByName(@Param("name") String name);
}
UserMapper.xml具体sql
<?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.zy.dao.UserMapper">
<select id="getUserByName" resultType="User">
select * from mybatis.user
where name=#{name}
</select>
</mapper>
UserService实现
public interface UserService {
// 根据姓名查询
User getUserByName(String name);
}
UserServiceImpl实现类
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByName(String name) {
return userMapper.getUserByName(name);
}
}
- 配置文件
application.properties
mybatis.type-aliases-package=com.zy.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
application.yaml
spring:
datasource:
username: root
password: zy123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
- 前端网页
index.html
<body th:align="center">
<h1>首页</h1>
<!--未登录显示登录-->
<shiro:notAuthenticated>
<a th:href="@{/toLogin}">登录</a>
</shiro:notAuthenticated>
<!--登陆之后显示注销-->
<shiro:authenticated>
用户名:<span th:text="${username}"></span> <br> <br>
<a th:href="@{/logout}">注销</a>
</shiro:authenticated>
<hr>
<a th:href="@{/user/show}">展示页面</a> <br>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">添加页面</a>
</div> <br>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">修改页面</a>
</div> <br>
</body>
login.html
<body th:align="center">
<h1>登录</h1>
<hr>
<p th:text="${msg}" style="color: red"></p>
<form method="post" action="/login">
用户名:<input type="text" name="username">
密码:<input type="password" name="pwd">
<input type="submit" value="提交">
</form>
</body>
add.html
<body align="center">
<h2>add页面</h2>
</body>
show.html
<body align="center">
<h2>show页面</h2>
</body>
update.html
<body align="center">
<h2>update页面</h2>
</body>