从外部来看Shiro,即从应用程序角度来观察如何使用shiro完成工作
1.导入日志相关依赖,版本自行加入
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>jcl-over-slf4jartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-log4j12artifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<scope>runtimescope>
dependency>
2.配置文件 log4j.properties shiro.ini
获取当前用户对象
Subject currentUser = SecurityUtils.getSubject();
通过当前用户拿到session
Session session = currentUser.getSession();
用户对象的常用方法:
//判断当前用户是否被认证
currentUser.isAuthenticated()
//获得当前用户的一个认证
currentUser.getPrincipal()
//当前用户是否拥有xx角色
currentUser.hasRole("schwartz")
//已登录用户是否具有某种权限
currentUser.isPermitted("lightsaber:wield")
令牌:
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
token.setRememberMe(true);//设置记住我功能
currentUser.login(token);//执行登录操作
currentUser.logout();//退出操作
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.0version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
<version>2.3.2.RELEASEversion>
dependency>
package com.nsx.shito.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;
/**
* Created by NXS
* 2020/9/1 17:06
*/
//自定义的 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;
}
}
package com.nsx.shito.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;
/**
* Created by NXS
* 2020/9/1 17:05
*/
@Configuration
public class ShiroConfig {
//shiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
factoryBean.setSecurityManager(securityManager);
return factoryBean;
}
//DefaultWebSecurityManager 2
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联userRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义类 1
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
}
package com.nsx.shito.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* Created by NXS
* 2020/9/1 16:08
*/
@Controller
public class LoginController {
@RequestMapping("/")
public String index(Model model) {
model.addAttribute("msg", "你好, shiro");
return "index";
}
@RequestMapping("/user/add")
public String add() {
return "/user/add";
}
@RequestMapping("/user/update")
public String update() {
return "/user/update";
}
}
index.html
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<h1>首页h1>
<p th:text="${msg}">p>
<a th:href="@{/user/add}">adda> | <a th:href="@{/user/update}">updatea>
body>
html>
剩下的自己自定义吧。。。。
//shiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
factoryBean.setSecurityManager(securityManager);
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有 记住我功能才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
//添加shiro的内置过滤器
Map<String, String> fiterMap = new LinkedHashMap<>();
// /user/* 指的是路径
fiterMap.put("/user/*", "authc");
factoryBean.setFilterChainDefinitionMap(fiterMap);
// 设置登录的请求
factoryBean.setLoginUrl("/login");
return factoryBean;
}
@RequestMapping("/login")
public String login() {
return "login";
}
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<form action="">
<p>用户名:<input type="text" name="username">p>
<p>密码:<input type="text" name="password">p>
<p><input type="submit">p>
form>
body>
html>
此时点击页面,发现点add update会跳转到一个登录页面,这是由于没有权限。
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.21version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.9version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.3version>
dependency>
spring:
datasource:
# 数据源基本配置
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_crud
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,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String password;
}
mybatis:
type-aliases-package: com.nsx.shiro
mapper-locations: classpath:mapper/*.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.nsx.shiro.dao.UserMapper">
<select id="getUserByName" parameterType="String" resultType="User">
select *
from user
where name = #{name}
</select>
</mapper>
package com.nsx.shiro.dao;
import com.nsx.shiro.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
/**
* Created by NXS
* 2020/9/2 12:15
*/
@Repository
@Mapper
public interface UserMapper {
User getUserByName(String name);
}
package com.nsx.shiro.service;
import com.nsx.shiro.pojo.User;
/**
* Created by NXS
* 2020/9/2 12:24
*/
public interface UserService {
User getUserByName(String name);
}
package com.nsx.shiro.service;
import com.nsx.shiro.dao.UserMapper;
import com.nsx.shiro.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* Created by NXS
* 2020/9/2 12:24
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByName(String name) {
return userMapper.getUserByName(name);
}
}
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("/login")
public String login(String name, String password, Model model) {
// 获取当前的用户
Subject subject = SecurityUtils.getSubject();
// 封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
try {
// 执行登录方法, 如果没有异常就说明用户和密码完全正确
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
// 用户名不存在
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {
// 密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
package com.nsx.shiro.config;
import com.nsx.shiro.pojo.User;
import com.nsx.shiro.service.UserService;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Created by NXS
* 2020/9/1 17:06
*/
//自定义的 UserRealm 处理数据
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
return null;
}
@Override
// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthorizationInfo");
// 封装用户的登录数据
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 连接数据库
User user = userService.getUserByName(userToken.getUsername());
// 没有此人,抛出 UnkownAccountException
if (user == null) {
return null;
}
/*三个参数:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials: 传递的密码对象
realmName: 认证名(指定当前 Realm 的类名)
*/
// 密码认证, shiro自己做
return new SimpleAuthenticationInfo("", user.getPassword(), "");
}
}
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<p th:text="${msg}" style="color: red">p>
<form th:action="@{/login}">
<p>用户名:<input type="text" name="name">p>
<p>密码:<input type="text" name="password">p>
<p><input type="submit">p>
form>
body>
html>
,认证已经做完了,但还有一个功能需要加,那就是MD5盐值加密了,这个功能写到了这篇文章。
那么认证完,就是说可以登进网页,但进入网页后,得根据用户有哪些权限去访问能访问的东西,所以接下来讲讲如何授权。
@RequestMapping("/unauthorize")
@ResponseBody
public String unauthorize() {
return "你没有权限访问此页面噢";
}
ShiroConfig
//shiroFilterFactoryBean 3
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
factoryBean.setSecurityManager(securityManager);
/*
anon:无需认证就可以访问
authc:必须认证了才能访问
user:必须拥有 记住我功能才能用
perms:拥有对某个资源的权限才能访问
role:拥有某个角色权限才能访问
*/
//添加shiro的内置过滤器 拦截
Map<String, String> fiterMap = new LinkedHashMap<>();
// 设置哪些路径授予哪些权限
fiterMap.put("/user/add", "perms[user:add]");
fiterMap.put("/user/update", "perms[user:update]");
// /user/* 指的是路径 必须认证了才能访问
fiterMap.put("/user/*", "authc");
factoryBean.setFilterChainDefinitionMap(fiterMap);
// 设置登录的请求
factoryBean.setLoginUrl("/toLogin");
// 跳转至未授权页面
factoryBean.setUnauthorizedUrl("/unauthorize");
return factoryBean;
}
UserRealm
//自定义的 UserRealm 处理数据
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
// 授权
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权doGetAuthorizationInfo");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
User currentUser = (User) subject.getPrincipal();
//设置当前用户的权限
info.addStringPermission(currentUser.getPermission());
return info;
}
@Override
// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthorizationInfo");
// 封装用户的登录数据
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 连接数据库
User user = userService.getUserByName(userToken.getUsername());
// 没有此人,抛出 UnkownAccountException
if (user == null) {
return null;
}
/*三个参数:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials: 传递的密码对象
realmName: 认证名(指定当前 Realm 的类名)
*/
// 密码认证, shiro自己做
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
}
<dependency>
<groupId>com.github.theborakompanionigroupId>
<artifactId>thymeleaf-extras-shiroartifactId>
<version>2.0.0version>
dependency>
// ShiroDialect 用来整合shiro和thymleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
@Override
// 认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了=>认证doGetAuthorizationInfo");
// 封装用户的登录数据
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
// 连接数据库
User user = userService.getUserByName(userToken.getUsername());
// 没有此人,抛出 UnkownAccountException
if (user == null) {
return null;
}
//获取当前登录的用户对象
Subject subject = SecurityUtils.getSubject();
//存放到Session中
Session session = subject.getSession();
session.setAttribute("user",user);
/*三个参数:
principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。
一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
credentials: 传递的密码对象
realmName: 认证名(指定当前 Realm 的类名)
*/
// 密码认证, shiro自己做
return new SimpleAuthenticationInfo(user, user.getPassword(), "");
}
控制层添加一个注销的方法
@RequestMapping("/logout")
public String logout() {
// 获取当前的用户
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.isAuthenticated()) {
currentUser.logout();
}
return "redirect:/toLogin";
}
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
head>
<body>
<h1>首页h1>
<div th:if="${session.user==null}">
<a th:href="@{/toLogin}">登录a>
div>
<div th:if="${session.user!=null}">
<a th:href="@{/logout}">注销a>
div>
<p th:text="${msg1}">p>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">adda>
div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">updatea>
div>
body>
html>
完,最后感谢你看完这篇文章