一、导入依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.7.1</version>
</dependency>
二、编写config
由于Shiro没有被SpringBoot所集成 所以我们要手动配置javaconfig
一共分为两个 shiroconfig用来进行核心文件的配置 userRealm用来进行用户的授权和登录验证
package com.llf.Config;
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
@Autowired
private DefaultWebSecurityManager SecurityManager;
@Bean
public ShiroFilterFactoryBean getshiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(SecurityManager);
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
三、用户拦截功能的实现
环境准备
- 首先将需要的页面准备好 首页 add页面 update页面
<!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>
<h1 >首页</h1>
<div th:text="${msg}"></div>
<h1><a th:href="@{/tohello}">hello</a></h1>
<h1><a th:href="@{/toadd}">add</a></h1>
<h1><a th:href="@{/toupdate}">update</a></h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>add</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>update</h1>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="text-align:center">欢迎光临</h1>
</body>
</html>
@org.springframework.stereotype.Controller
public class Controller {
@RequestMapping("/")
public String toindex(Model model){
model.addAttribute("msg","hello shhiro");
return "index";
}
@RequestMapping("/toadd")
public String toadd(){
return "user/add";
}
@RequestMapping("/toupdate")
public String toupdate(){
return "user/update";
}
@RequestMapping("/tohello")
public String tohello(){
return "user/hello";
}
}
访问权限设置
现在我们约定,只有登录的人才能访问所有页面
于是我们需要在shiroconfig中进行配置
Map<String, String> FilterMap = new LinkedHashMap<>();
FilterMap.put("/user/**","authc");
只有认证的人才能访问user包下的所有文件
package com.llf.Config;
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
@Autowired
private DefaultWebSecurityManager SecurityManager;
@Bean
public ShiroFilterFactoryBean getshiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(SecurityManager);
Map<String, String> FilterMap = new LinkedHashMap<>();
FilterMap.put("/user/hello","authc");
bean.setFilterChainDefinitionMap(FilterMap);
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
}
四、 登录页面跳转
访问权限受阻,我们应该让它自动跳到登录页面进项登录验证,于是我们被迫写一个login页面和login请求
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>
<div th:text="${gg}"style="color: red"></div>
<form th:action="@{/login}" method="post">
用户名:<input type="text" name="username" onautocomplete="false"><br/>
密 码:<input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
</h1>
</body>
</html>
@RequestMapping("/tologin")
public String tologin(){
return "user/login";
}
然后在配置类中配置登录页面
bean.setLoginUrl("/tologin");
package com.llf.Config;
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
@Autowired
private DefaultWebSecurityManager SecurityManager;
@Bean
public ShiroFilterFactoryBean getshiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(SecurityManager);
Map<String, String> FilterMap = new LinkedHashMap<>();
FilterMap.put("/user/hello","authc");
bean.setFilterChainDefinitionMap(FilterMap);
bean.setLoginUrl("/tologin");
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
整合mybatis
登录就涉及到账号密码等问题,所以我们还得链接数据库 整合mybatis
-
导入依赖、配置数据源
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>然后就是数据源配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mydb1?serverTimezone=UTC
username: root
password: 123456
type: com.alibaba.druid.pool.DruidDataSource
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,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
type-aliases-package: com.llf.Pojo
mapper-locations: classpath:Mapper/*.xml
-
编写Pojo Mapper、Service 。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
数据验证
@RequestMapping("/login")
public String login(String username, String password, Model model, HttpSession session){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
subject.login(token);
session.setAttribute("username",username);
return "index";
}catch ( UnknownAccountException e){
model.addAttribute("gg","用户名不存在");
return "user/login";
}catch (IncorrectCredentialsException e){
model.addAttribute("gg","密码错误");
return "user/login";
}
抛出的异常 我们只需要将其名字写出 并指定如何跳转 shrio会自动对其进行判断错误类型
-
在UserRealm对前端的数据和数据库中的数据进行匹配 @Autowired
private ShiroServiceImpl shiroService;
Shiro user = shiroService.quaryByName(usertoken.getUsername());
if (user == null){
return null;
}
返回null shiro会自动根据我们抛出的异常判断错误类型
五、注销
登录成功之后就应该有注销功能, 这个功能Shiro也帮我们做好了 我们只需要将页面和请求写好即可
<a th:href="@{/logout}" ><button class="btn btn-sm btn-success" >注销</button></a>
编写controller请求
@RequestMapping("/logout")
public String logout(){
return "index";
在配置类中配置注销
FilterMap.put("/logout","logout");
package com.llf.Config;
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
@Autowired
private DefaultWebSecurityManager SecurityManager;
@Bean
public ShiroFilterFactoryBean getshiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(SecurityManager);
Map<String, String> FilterMap = new LinkedHashMap<>();
FilterMap.put("/user/hello","authc");
FilterMap.put("/logout","logout");
bean.setFilterChainDefinitionMap(FilterMap);
bean.setLoginUrl("/tologin");
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
六、个性化定制
按照常理 我们应该在没有登录的时候显示登录按钮 登录之后显示注销按钮 那么我们应该怎么做?
导入thymeleaf–shiro整合包
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
将ShiroDialec注入Bean
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
在登录验证成功的时候注入session
在controller的登录判断中 登陆成功 创建一个session
session.setAttribute("username",username);
@RequestMapping("/login")
public String login(String username, String password, Model model, HttpSession session){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try{
subject.login(token);
session.setAttribute("username",username);
return "index";
}catch ( UnknownAccountException e){
model.addAttribute("gg","用户名不存在");
return "user/login";
}catch (IncorrectCredentialsException e){
model.addAttribute("gg","密码错误");
return "user/login";
}
}
在index.html页面引入thymeleaf命名空间
xmlns:th="http://www.thymeleaf.org"
在登录、注销的div中进行th:if判断 看session是否为空 如果为空就显示登录按钮 如果不为空 就显示注销按钮
<div th:if="${session.username}==null" >
<a th:href="@{/tologin}" ><button class="btn btn-sm btn-success" >登录</button></a>
</div>
<div th:if="${session.username}" >
<a th:href="@{/logout}" ><button class="btn btn-sm btn-success" >注销</button></a>
</div>
七、不同权限的人显示不同页面
在页面初始化阶段 ,我们写了几个页面 但是在截图中 却没有看到add、update页面 这是怎么做到的呢?
首先导入thymeleaf–shiro整合包
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
将ShiroDialec注入Bean
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
在UserRealm类中进行权限授予
Subject subject = SecurityUtils.getSubject();
Shiro currentUser = (Shiro) subject.getPrincipal();
info.addStringPermission(currentUser.getRole());
在config类中进行权限访问设置
FilterMap.put("/toadd","perms[shiro:add]");
FilterMap.put("//toupdate","perms[shiro:update]");
在index里进行显示设置
- 首先导入thymeleaf与shiro整合的命名空间
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
观察它与thymeleaf和Security整合的命名空间有什么区别?
区别就是 它是由Thymeleaf命名空间 +/ +各自整合包的名字
-
在add、update页面的div中进行判断
<div shiro:hasPermission="shiro:add">
<h1>
<a th:href="@{/toadd}">add
</a>
</h1>
</div>
<div shiro:hasPermission="shiro:update">
<h1>
<a th:href="@{/toupdate}">update
</a>
</h1>
</div>
由于用户在登录的时候 我们从数据库中将其拥有的权限赋予了他 所以在这我们就可以进行判断
根据不同的权限 我们就让其显示了不同的页面
注意
这里有一个坑 我们页面的访问请求和权限设置的请求一定要和Controller中的mapping请求一致 要不然就会出现任何人都能访问任何页面的bug
配置类总代码
package com.llf.Config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Autowired
private UserRealm userRealm;
@Autowired
private DefaultWebSecurityManager SecurityManager;
@Bean
public ShiroFilterFactoryBean getshiroFilterFactoryBean(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(SecurityManager);
Map<String, String> FilterMap = new LinkedHashMap<>();
FilterMap.put("/user/hello","user");
FilterMap.put("/toadd","perms[shiro:add]");
FilterMap.put("//toupdate","perms[shiro:update]");
FilterMap.put("/logout","logout");
bean.setFilterChainDefinitionMap(FilterMap);
bean.setUnauthorizedUrl("/unauthorized");
bean.setLoginUrl("/tologin");
return bean;
}
@Bean
public DefaultWebSecurityManager getdefaultWebSecurityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean
public UserRealm userRealm(){
return new UserRealm();
}
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
UserRealm
package com.llf.Config;
import com.llf.Pojo.Shiro;
import com.llf.Service.ShiroServiceImpl;
import org.apache.catalina.User;
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.session.Session;
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 ShiroServiceImpl shiroService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info= new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();
Shiro currentUser = (Shiro) subject.getPrincipal();
info.addStringPermission(currentUser.getRole());
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken usertoken = (UsernamePasswordToken) token;
Shiro user = shiroService.quaryByName(usertoken.getUsername());
if (user == null){
return null;
}
return new SimpleAuthenticationInfo(user,user.getPassword(),"");
}
}
|