文章目录
maven坐标
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
shiro的config类
@Configuration
public class ShiroConfig {
// 1.创建 shiro filter
// filter 负责拦截请求的
@Bean
public ShiroFilterFactoryBean getShiroFilterBean(DefaultWebSecurityManager defaultSecurityManager) {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
// 给filter设置安全管理器
filter.setSecurityManager(defaultSecurityManager);
Map<String, String> map = new HashMap<String, String>();
// 配置系统的公共资源
map.put("/user/login","anon");
// 配置系统的受限资源,authc是shiro验证过滤器的缩写,代表这个路径需要验证登录
map.put("/index", "authc");
// 代表系统内的所有资源都是受限资源
map.put("/**","authc");
// 将受限资源名单和公共资源名单告诉shiro
filter.setFilterChainDefinitionMap(map);
// 默认登录页面,当用户未认证时,shiro就会将用户指向当前路径
filter.setLoginUrl("/user/login");
return filter;
}
// 2.创建 securityMananger
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 给securityMananger设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
// 3.创建 自定义realm
@Bean
public Realm getRealm() {
return new CustomerRealm();
}
}
shiro中常用的过滤器
配置缩写 | 对应的过滤器 | 功能 |
---|---|---|
anon | AnonymousFilter | 指定url可以匿名访问 |
authc | FormAuthenticationFilter | 指定url可以匿名访问指定url需要form表单登录,默认会从请求中获取username . password , rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑,但是一般都是我们自己在控制器写登录逻辑的,自己写的话出错返回的信息都可以定制嘛。 |
authcBasic | BasicHttpAuthenticationFilter | 指定url需要basic登录 |
logout | LogoutFilter | 登出过滤器,配置指定url就可以实现退出功能,非常方便 |
noSessionCreation | NoSessionCreationFilter | 禁止创建会话 |
perms | PermissionsAuthorizationFilter | 需要指定权限才能访问 |
port | PortFilter | 需要指定端口才能访问 |
roles | RolesAuthorizationFilter | 需要指定角色才能访问 |
ssl | SslFilter | 需要https请求才能访问 |
user | UserFilter | 需要已登录或“记住我”的用户才能访问 |
完整实例
对用户注册进行md5+salt的编码
对用户登录进行md5+salt的验证
maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jstl表达式支持包 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- jsp引擎 -->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<version>9.0.38</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<!-- mybatis启动器 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<!-- jdbc -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.1</version>
</dependency>
</dependencies>
application.properties配置
server.port=8080
server.servlet.context-path=/p01
# jsp文件所在路径
spring.mvc.view.prefix=/WEB-INF/jsp/
# jsp文件的后缀名
spring.mvc.view.suffix=.jsp
# 日志配置
logging.level.root=INFO
logging.level.cn.liuhao.springboot_shiro01.**=DEBUG
#数据连接参数
spring.datasource.driveClassName=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.23.128:3306/test_01?useSSL=false&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=123456
#数据库连接池
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#指定mybatis要起别名实体类所在的包
mybatis.type-aliases-package=cn.liuhao.springboot_shiro01.pojo
#指定mapper映射文件的文件夹路径
mybatis.mapperLocations=classpath:mappers/*.xml
数据库表
user实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class User {
private Integer u_id;
private String u_name;
private String u_pwd;
private String u_salt;
// 一个用户可以拥有多条权限
private List<Permisson> permissons;
}
mapper inteface与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="cn.liuhao.springboot_shiro01.mapper.UserMapper">
<insert id="insert" parameterType="user">
insert into user values (default,#{u_name},#{u_pwd},#{u_salt})
</insert>
<select id="selectByName" resultType="user" parameterType="java.lang.String">
select * from user where u_name=#{username}
</select>
<resultMap type="User" id="userWithRole">
<id column="u_id" property="u_id" />
<result column="u_name" property="u_name" />
<result column="u_pwd" property="u_pwd" />
<result column="u_salt" property="u_salt" />
<collection property="permissons" ofType="cn.liuhao.springboot_shiro01.pojo.Permisson" >
<id column="p_id" property="p_id" />
<result column="p_name" property="p_name" />
</collection>
</resultMap>
<select id="findUserWithRoleByName" resultMap="userWithRole" parameterType="String" >
select * from user_permisson_view where u_name=#{username}
</select>
</mapper>
UserMapper
@Mapper
public interface UserMapper {
/**
* 添加用户
*
* @param user
* @return
*/
public int insert(User user);
/**
* 查找用户
*
* @param name
* @return
*/
public User selectByName(@Param(value = "username") String username);
/**
* 通过用户名查找用户,并将用户的权限一起查出来
*
* @param name
* @return
*/
public User findUserWithRoleByName(@Param(value = "username") String name);
}
service层
UserServiceImpl要先定义一个UserService接口
@Transactional
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public boolean addUser(User user) {
user.setU_salt(SaltUtil.getRandomSalt(10));
// 使用md5+salt的方式对密码进行编码
String hex_pwd = new Md5Hash(user.getU_pwd(), user.getU_salt(), 1024).toHex();
// 将密文存入进数据库
user.setU_pwd(hex_pwd);
return userMapper.insert(user) > 0 ? true : false;
}
@Override
public User findUserByName(String name) {
return userMapper.selectByName(name);
}
@Override
public User getUserWithPermisson(String username) {
return userMapper.findUserWithRoleByName(username);
}
}
ShiroConfig
@Configuration
public class ShiroConfig {
@Autowired
private CustomerRealm realm;
// 1.创建 shiro filter
// filter 负责拦截请求的
@Bean
public ShiroFilterFactoryBean getShiroFilterBean(DefaultWebSecurityManager defaultSecurityManager) {
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
// 给filter设置安全管理器
filter.setSecurityManager(defaultSecurityManager);
Map<String, String> map = new HashMap<String, String>();
// 配置系统的受限资源
map.put("/index", "authc");
// 配置系统的公共资源
// 将受限资源名单和公共资源名单告诉shiro
filter.setFilterChainDefinitionMap(map);
// 默认登录页面,当用户未认证时,shiro就会将用户指向当前路径
filter.setLoginUrl("login");
return filter;
}
// 2.创建 securityMananger
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(CustomerRealm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
// 创建realm使用的hash凭证器
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
// 告诉凭证器加密的类型
matcher.setHashAlgorithmName("md5");
// 告诉凭证器使用hash散列加密的次数
matcher.setHashIterations(1024);
// 注入realm
realm.setCredentialsMatcher(matcher);
// 给securityMananger设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
}
customRealm
@Slf4j
@Component
public class CustomerRealm extends SimpleAccountRealm {
@Autowired
private UserService userService;
/**
*
* 1.从数据库根据名称查找用户,将查找出来的数据与用户提交的进行比对
*
*
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken principal = (UsernamePasswordToken) token;
// 从token中给出的信息去查找用户
User user = userService.findUserByName(principal.getUsername());
// 不存在用户则返回null
if (user == null) {
log.debug("未能找到与用户名" + principal.getUsername() + "相符合的账户");
return null;
}
// 创建authenticationInfo返回
// 参数1 是用户的身份
// 参数2 从数据库取出的经过md5和slat加工之后的密码
// 参数3 从数据库取出用户的slat
// 参数4 当前realm的name
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal.getPrincipal(),
user.getU_pwd(), ByteSource.Util.bytes(user.getU_salt()), this.getName());
return authenticationInfo;
}
/**
* 根据传递过来的principal从数据库内取出对应用户所有的权限
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
// 从数据库中取出对应用户名的用户信息与权限信息
User userWithPermisson = userService.getUserWithPermisson(username);
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
// 将权限信息放进authorizationInfo传递给secureMananger
for (Permisson permisson : userWithPermisson.getPermissons()) {
authorizationInfo.addStringPermission(permisson.getP_name());
}
return authorizationInfo;
}
}
Controller
@Controller
public class UserController {
@Autowired
private UserService userService;
@RequestMapping("{page_name}")
public String fun01(@PathVariable("page_name") String page_name) {
return page_name;
}
/**
* 登录验证
*
* @param uname
* @param password
* @return
*/
@RequestMapping("/user/check")
public String login(@RequestParam String uname, @RequestParam String password) {
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(uname, password));
return "index";
} catch (AuthenticationException e) {
e.printStackTrace();
return "login";
}
}
@RequestMapping("/user/logout")
public String logout() {
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
@RequestMapping("/user/registerCheck")
public ModelAndView registerCheck(@RequestParam("uname") String uname, @RequestParam("upwd") String upwd) {
ModelAndView view = new ModelAndView();
boolean addUser = userService.addUser(new User(null, uname, upwd, null, null));
view.setViewName(addUser == true ? "login" : "register");
return view;
}
}
ProductController
@RequestMapping("product")
public class ProductController {
@RequestMapping("insert")
public String insert() {
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("product:insert")) {
return "<h1>insert.product.page</h1>";
}
return "<h1>用户无权限</h1>";
}
@RequestMapping("update")
public String update() {
Subject subject = SecurityUtils.getSubject();
if (subject.isPermitted("product:update")) {
return "<h1>update.product.page</h1>";
}
return "<h1>用户无权限</h1>";
}
}
结果展示
用户验证截图
用户授权截图