(1)Shiro介绍
Shiro简介:Shiro是一个强大的简单易用的Java安全框架,主要用来更便捷的认证,授权,加密,会话管理。Shiro首要的和最重要的目标就是容易使用并且容易理解,通过Shiro易于理解的API,您可以快速、轻松地获得任何应用程序——从最小的移动应用程序最大的网络和企业应用程序。
Shiro的架构有三个主要概念:Subject
, SecurityManager
和 Realms
Subject: 当前参与应用安全部分的主角。可以是用户,可以试第三方服务,可以是cron 任务,或者任何东西。主要指一个正在与当前软件交互的东西。所有Subject都需要SecurityManager,当你与Subject进行交互,这些交互行为实际上被转换为与SecurityManager的交互。
SecurityManager:安全管理员,Shiro架构的核心,它就像Shiro内部所有原件的保护伞。然而一旦配置了SecurityManager,SecurityManager就用到的比较少,开发者大部分时间都花在Subject上面。当你与Subject进行交互的时候,实际上是SecurityManager在背后帮你举起Subject来做一些安全操作。
Realms: Realms作为Shiro和你的应用的连接桥,当需要与安全数据交互的时候,像用户账户,或者访问控制,Shiro就从一个或多个Realms中查找。Shiro提供了一些可以直接使用的Realms,如果默认的Realms不能满足你的需求,你也可以定制自己的Realms。
(2)SpringBoot中使用Shiro
(1)环境的配置
这里连接数据库来进行账户名,密码的比对,采用mybatis框架
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
(2)pojo实体类,service层,mapper及相关配置文件xml的编写
(1)pojo层:
这里要和数据库中的字段相对应
package com.csh.springbootshiro.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private int id;
private String username;
private String password;
private String perms;
}
相对应的数据库:
(2)mapper及配置文件
package com.csh.springbootshiro.mapper;
import com.csh.springbootshiro.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface UserMapper {
public User findbyname(String name);
}
对应的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.csh.springbootshiro.mapper.UserMapper">
<select id="findbyname" parameterType="String" resultType="User">
select * from person where username=#{name}
</select>
</mapper>
(3)service层
package com.csh.springbootshiro.service;
import com.csh.springbootshiro.pojo.User;
public interface UserService {
public User findbyname(String name);
}
package com.csh.springbootshiro.service;
import com.csh.springbootshiro.mapper.UserMapper;
import com.csh.springbootshiro.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService{
@Autowired
UserMapper userMapper;
@Override
public User findbyname(String name) {
return userMapper.findbyname(name);
}
}
(3)视图控制层(要有账户密码错误的反馈信息)和shiro配置类的编写
ShiroConfig配置类的编写:
package com.csh.springbootshiro.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.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class ShiroConfig {
//创建 ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
/*
添加Shiro内置过滤器,常用的有如下过滤器:
anon: 无需认证就可以访问
authc: 必须认证才可以访问
user: 如果使用了记住我功能就可以直接访问
perms: 拥有某个资源权限才可以访问
role: 拥有某个角色权限才可以访问
*/
HashMap<String, String> map = new HashMap<>();//这里进行的是每个url的访问权限限制
// map.put("/user/add","authc");
// map.put("/user/update","authc");
map.put("/user/add","perms[user:add]");
map.put("/user/update","perms[user:update]");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setLoginUrl("/tologin");//这里是点击登录是发送一个请求来到登录界面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");//无权访问是发送一个请求来到指定界面
return shiroFilterFactoryBean;
}
//创建 DefaultWebSecurityManage
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm)
{
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(userRealm);
return defaultWebSecurityManager;
}
// 创建 realm 对象,在建个类来继承AuthorizingRealm这个类
@Bean
public UserRealm userRealm()
{
return new UserRealm();
}
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
UserReaml类:
package com.csh.springbootshiro.config;
import com.csh.springbootshiro.pojo.User;
import com.csh.springbootshiro.service.UserService;
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.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了=>授权逻辑PrincipalCollection");
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
Subject subject = SecurityUtils.getSubject();//总的来说,SecurityUtils.getSubject()是每个请求创建一个Subject, 并保存到ThreadContext的resources(ThreadLocal<Map<Object, Object>>)变量中,也就是一个http请求一个subject,并绑定到当前线程
User user = (User) subject.getPrincipal();//SecurityUtils.getSubject().getPrincipal()获取封装的用户信息
subject.getSession().setAttribute("loginuser",user);//登录之后才会显示相关的页面资源,游客访问的话只有一个登录按钮
simpleAuthorizationInfo.addStringPermission(user.getPerms());
return simpleAuthorizationInfo;
}
@Override
//这里是在登录界面进行的
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//AuthenticationToken authenticationToken拿到的是视图控制层传来的,UsernamePasswordToken 是用来存储用户和密码的
System.out.println("执行了=>认证逻辑AuthenticationToken");
UsernamePasswordToken usernamePasswordToken= (UsernamePasswordToken) authenticationToken;
User user = userService.findbyname(usernamePasswordToken.getUsername());//在数据库中进行查找该用户
if(user==null)
{
return null;//不存在该用户,返回null
}
else
{
return new SimpleAuthenticationInfo(user,user.getPassword(),"");//这里进行密码的判断,在视图控制层返回相关的信息
}
}
}
controller:
package com.csh.springbootshiro.controller;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyController {
@RequestMapping({"/","/index"})
public String index(){
return "index";
}
@RequestMapping("/tologin")
public String login()
{
return "login";
}
@RequestMapping("/user/add")
public String add()
{
return "user/add";
}
@RequestMapping("/user/update")
public String update()
{
return "user/update";
}
@RequestMapping("/login")
public String login(String username, String password, Model model)
{
//这里拿到了登录表单传来的用户名和密码
Subject subject= SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
try{
subject.login(usernamePasswordToken);//来进行信息的匹配
return "index";//登录成功
}catch (UnknownAccountException e)//UnknownAccountException用户不存在,返回的null
{
model.addAttribute("msg","用户不存在");//反馈给前端的登录界面
return "login";
}catch (IncorrectCredentialsException e)//IncorrectCredentialsException 密码错误
{
model.addAttribute("msg","用户密码错误");
return "login";
}
}
@RequestMapping("/noauth")
@ResponseBody
public String noauth()
{
return "未经许可,不可授权";
}
}
(3)成果展示