-
org.apache.shiro -
shiro-spring-boot-web-starter -
1.9.0 -
org.springframework.boot -
spring-boot-starter-thymeleaf
- shiro:
- loginUrl: /myController/login
-
- 或者
-
- shiro.loginUrl=/myController/login
- CREATE DATABASE IF NOT EXISTS `shirodb` CHARACTER SET utf8mb4;
- USE `shirodb`;
- CREATE TABLE `user` (
- `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
- `name` VARCHAR(30) DEFAULT NULL COMMENT '用户名',
- `pwd` VARCHAR(50) DEFAULT NULL COMMENT '密码',
- `rid` BIGINT(20) DEFAULT NULL COMMENT '角色编号',
- PRIMARY KEY (`id`)
- ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='用户表';
- import lombok.AllArgsConstructor;
- import lombok.Data;
- import lombok.NoArgsConstructor;
-
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- public class User {
- private Integer id;
- private String name;
- private String pwd;
- private Integer rid;
- }
- import com.baomidou.mybatisplus.core.mapper.BaseMapper;
- import com.example.demo.model.User;
- import org.springframework.stereotype.Repository;
-
- @Repository
- public interface UserMapper extends BaseMapper<User> {
- }
- import com.example.demo.model.User;
-
- public interface UserService {
- //用户登录
- User getUserInfoByName(String name);
- }
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.example.demo.mapper.UserMapper;
- import com.example.demo.model.User;
- import com.example.demo.service.UserService;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Service;
-
- @Service
- public class UserServiceImpl implements UserService {
- @Autowired
- private UserMapper userMapper;
- @Override
- public User getUserInfoByName(String name) {
- QueryWrapper
wrapper = new QueryWrapper<>(); - wrapper.eq("name",name);
- User user = userMapper.selectOne(wrapper);
- return user;
- }
- }
- import com.example.demo.model.User;
- import com.example.demo.service.UserService;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationInfo;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.SimpleAuthenticationInfo;
- import org.apache.shiro.authz.AuthorizationInfo;
- import org.apache.shiro.realm.AuthorizingRealm;
- import org.apache.shiro.subject.PrincipalCollection;
- import org.apache.shiro.util.ByteSource;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
-
- @Component
- public class MyRealm extends AuthorizingRealm {
- @Autowired
- private UserService userService;
- //自定义授权方法
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- return null;
- }
- //自定义登录认证方法
- @Override
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
- //1 获取用户身份信息
- String name = token.getPrincipal().toString();
- //2 调用业务层获取用户信息(数据库中)
- User user = userService.getUserInfoByName(name);
- //3 判断并将数据完成封装
- if(user!=null){
- AuthenticationInfo info = new SimpleAuthenticationInfo(
- token.getPrincipal(),
- user.getPwd(),
- ByteSource.Util.bytes("salt"),
- token.getPrincipal().toString()
- );
- return info;
- }
- return null;
- }
- }
- import com.example.demo.component.MyRealm;
- import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
- import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
- 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;
-
- @Configuration
- public class ShiroConfig {
- @Autowired
- private MyRealm myRealm;
-
- //配置 SecurityManager
- @Bean
- public DefaultWebSecurityManager defaultWebSecurityManager() {
- //1 创建 defaultWebSecurityManager 对象
- DefaultWebSecurityManager defaultWebSecurityManager = new
- DefaultWebSecurityManager();
- //2 创建加密对象,并设置相关属性
- HashedCredentialsMatcher matcher = new
- HashedCredentialsMatcher();
- //2.1 采用 md5 加密
- matcher.setHashAlgorithmName("md5");
- //2.2 迭代加密次数
- matcher.setHashIterations(3);
- //3 将加密对象存储到 myRealm 中
- myRealm.setCredentialsMatcher(matcher);
- //4 将 myRealm 存入 defaultWebSecurityManager 对象
- defaultWebSecurityManager.setRealm(myRealm);
- //5 返回
- return defaultWebSecurityManager;
- }
-
- //配置 Shiro 内置过滤器拦截范围
- @Bean
- public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
- DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
- //设置不认证可以访问的资源
- definition.addPathDefinition("/myController/userLogin", "anon");
- definition.addPathDefinition("/login", "anon");
- //设置需要进行登录认证的拦截范围
- definition.addPathDefinition("/**", "authc");
- return definition;
- }
- }
- import org.apache.shiro.SecurityUtils;
- import org.apache.shiro.authc.AuthenticationException;
- import org.apache.shiro.authc.AuthenticationToken;
- import org.apache.shiro.authc.UsernamePasswordToken;
- import org.apache.shiro.subject.Subject;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
-
- @Controller
- @RequestMapping("myController")
- public class MyController {
- @GetMapping("userLogin")
- @ResponseBody
- public String userLogin(String name,String pwd){
- //1 获取 Subject 对象
- Subject subject = SecurityUtils.getSubject();
- //2 封装请求数据到 token 对象中
- AuthenticationToken token = new UsernamePasswordToken(name,pwd);
- //3 调用 login 方法进行登录认证
- try {
- subject.login(token);
- return "登录成功";
- } catch (AuthenticationException e) {
- e.printStackTrace();
- System.out.println("登录失败");
- return "登录失败";
- }
- }
- }
确认数据库密码是加盐 3 次加密密码



- "en">
- "UTF-8">
-
Title Shiro 登录认证
- 用户名:"text" name="name" value="">
- 密码:"password" name="pwd" value="">
- "submit" value="登录">
- "en" xmlns:th="http://www.thymeleaf.org">
- "UTF-8">
-
Title Shiro 登录认证后主页面
- 登录用户为:"${session.user}">
- //跳转登录页面
- @GetMapping("login")
- public String login(){
- return "login";
- }
- //登录认证
- @GetMapping("userLogin")
- public String userLogin(String name, String pwd, HttpSession session){
- //1 获取 Subject 对象
- Subject subject = SecurityUtils.getSubject();
- //2 封装请求数据到 token 对象中
- AuthenticationToken token = new UsernamePasswordToken(name,pwd);
- //3 调用 login 方法进行登录认证
- try {
- subject.login(token);
- session.setAttribute("user",token.getPrincipal().toString());
- return "main";
- } catch (AuthenticationException e) {
- e.printStackTrace();
- System.out.println("登录失败");
- return "登录失败";
- }
- }

启动访问测试 localhost:8080/myController/login


|
AuthenticationStrategy class
|
描述
|
|
AtLeastOneSuccessfulStrategy
|
只要有一个(或更多)的
Realm
验证成功,那么认证将视为成功
|
|
FirstSuccessfulStrategy
|
第一个
Realm
验证成功,整体认证将视为成功,且后续
Realm
将被忽略
|
|
AllSuccessfulStrategy
|
所有
Realm
成功,认证才视为成功
|
- //配置 SecurityManager
- @Bean
- public DefaultWebSecurityManager defaultWebSecurityManager(){
- //1 创建 defaultWebSecurityManager 对象
- DefaultWebSecurityManager defaultWebSecurityManager = new
- DefaultWebSecurityManager();
- //2 创建认证对象,并设置认证策略
- ModularRealmAuthenticator modularRealmAuthenticator = new
- ModularRealmAuthenticator();
- modularRealmAuthenticator.setAuthenticationStrategy(new
- AllSuccessfulStrategy());
-
- defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator)
- ;
- //3 封装 myRealm 集合
- List
list = new ArrayList<>(); - list.add(myRealm);
- list.add(myRealm2);
-
- //4 将 myRealm 存入 defaultWebSecurityManager 对象
- defaultWebSecurityManager.setRealms(list);
- //5 返回
- return defaultWebSecurityManager;
- }
Shiro 提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器, 下次再打开时还是能记住你是谁, 下次访问时无需再登录即可访问。
- import com.example.demo.component.MyRealm;
- import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
-
- import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
- import org.apache.shiro.web.mgt.CookieRememberMeManager;
- import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
- import org.apache.shiro.web.servlet.SimpleCookie;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- public class ShiroConfig {
- @Autowired
- private MyRealm myRealm;
-
- //配置 SecurityManager
- @Bean
- public DefaultWebSecurityManager defaultWebSecurityManager() {
- //1 创建 defaultWebSecurityManager 对象
- DefaultWebSecurityManager defaultWebSecurityManager = new
- DefaultWebSecurityManager();
- //2 创建加密对象,并设置相关属性
- HashedCredentialsMatcher matcher = new
- HashedCredentialsMatcher();
- //2.1 采用 md5 加密
- matcher.setHashAlgorithmName("md5");
- //2.2 迭代加密次数
- matcher.setHashIterations(3);
- //3 将加密对象存储到 myRealm 中
- myRealm.setCredentialsMatcher(matcher);
- //4 将 myRealm 存入 defaultWebSecurityManager 对象
- defaultWebSecurityManager.setRealm(myRealm);
- //4.5 设置 rememberMe
- defaultWebSecurityManager.setRememberMeManager(rememberMeManager());
- //5 返回
- return defaultWebSecurityManager;
- }
- //cookie 属性设置
- public SimpleCookie rememberMeCookie(){
- SimpleCookie cookie = new SimpleCookie("rememberMe");
- //设置跨域
- //cookie.setDomain(domain);
- cookie.setPath("/");
- cookie.setHttpOnly(true);
- cookie.setMaxAge(30*24*60*60);
- return cookie;
- }
- //创建 Shiro 的 cookie 管理对象
- public CookieRememberMeManager rememberMeManager(){
- CookieRememberMeManager cookieRememberMeManager = new
- CookieRememberMeManager();
- cookieRememberMeManager.setCookie(rememberMeCookie());
-
- cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
- return cookieRememberMeManager;
- }
-
-
- //配置 Shiro 内置过滤器拦截范围
- @Bean
- public DefaultShiroFilterChainDefinition shiroFilterChainDefinition() {
- DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
- //设置不认证可以访问的资源
- definition.addPathDefinition("/myController/userLogin", "anon");
- definition.addPathDefinition("/myController/login", "anon");
- definition.addPathDefinition("/login", "anon");
- //设置需要进行登录认证的拦截范围
- definition.addPathDefinition("/**", "authc");
- //添加存在用户的过滤器(rememberMe)
- definition.addPathDefinition("/**","user");
- return definition;
- }
-
- }
- //登录认证
- @GetMapping("userLogin")
- public String userLogin(String name, String pwd,@RequestParam(defaultValue =
- "false")boolean rememberMe, HttpSession session){
- //1 获取 Subject 对象
- Subject subject = SecurityUtils.getSubject();
- //2 封装请求数据到 token 对象中
- AuthenticationToken token = new UsernamePasswordToken(name,pwd,rememberMe);
- //3 调用 login 方法进行登录认证
- try {
- subject.login(token);
- session.setAttribute("user",token.getPrincipal().toString());
- return "main";
- } catch (AuthenticationException e) {
- e.printStackTrace();
- System.out.println("登录失败");
- return "登录失败";
- }
- }
- //登录认证验证 rememberMe
- @GetMapping("userLoginRm")
- public String userLogin(HttpSession session) {
- session.setAttribute("user","rememberMe");
- return "main";
- }
登录勾选记住用户
重新访问 userLoginRm http://localhost:8080/myController/userLoginRm

-
Shiro 登录认证后主页面
-
- 登录用户为:"${session.user}">
-
- //配置 Shiro 内置过滤器拦截范围
- @Bean
- public DefaultShiroFilterChainDefinition shiroFilterChainDefinition(){
- DefaultShiroFilterChainDefinition definition = new
- DefaultShiroFilterChainDefinition();
- //设置不认证可以访问的资源
- definition.addPathDefinition("/myController/userLogin","anon");
- definition.addPathDefinition("/myController/login","anon");
- //配置登出过滤器
- definition.addPathDefinition("/logout","logout");
- //设置需要进行登录认证的拦截范围
- definition.addPathDefinition("/**","authc");
- //添加存在用户的过滤器(rememberMe)
- definition.addPathDefinition("/**","user");
- return definition;
- }

|
@RequiresAuthentication
|
验证用户是否登录,等同于方法subject.isAuthenticated()
|
|
@RequiresUser
|
验证用户是否被记忆:
登录认证成功subject.isAuthenticated()为true
登录后被记忆subject.isRemembered()为true
|
|
@RequiresGuest
|
验证是否是一个guest的请求,是否是游客的请求此时subject.getPrincipal()为null
|
|
@RequiresRoles
|
验证subject是否有相应角色,有角色访问方法,没有则会抛出异常 AuthorizationException。
|
|
@RequiresPermissions
|
验证subject是否有相应权限,有权限访问方法,没有则会抛出异常
AuthorizationException。
|
- //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
- @Override
- protected AuthorizationInfo
- doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- //2 存储角色
- info.addRole("admin");
- //返回
- return info;
- }


- CREATE TABLE `role` (
- `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
- `name` VARCHAR(30) DEFAULT NULL COMMENT '角色名',
- `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
- `realname` VARCHAR(20) DEFAULT NULL COMMENT '角色显示名',
- PRIMARY KEY (`id`)
- ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色表';

- CREATE TABLE `role_user` (
- `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
- `uid` BIGINT(20) DEFAULT NULL COMMENT '用户 id',
- `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
- PRIMARY KEY (`id`)
- ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色用户映射表';

- SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE
- uid=(SELECT id FROM USER WHERE NAME='张三'));
- @Select("SELECT NAME FROM role WHERE id IN (SELECT rid FROM role_user WHERE uid=(SELECT id FROM USER WHERE NAME=#{principal}))")
- List
getUserRoleInfoMapper(@Param("principal") String principal);
- //获取用户的角色信息
- @Override
- public List
getUserRoleInfo(String principal) { - return userMapper.getUserRoleInfoMapper(principal);
- }
- //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("进入自定义授权方法");
- //获取当前用户身份信息
- String principal =
- principalCollection.getPrimaryPrincipal().toString();
- //调用接口方法获取用户的角色信息
- List
roles = userService.getUserRoleInfo(principal); - System.out.println("当前用户角色信息:"+roles);
- //创建对象,存储当前登录的用户的权限和角色
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- //存储角色
- info.addRoles(roles);
- //返回
- return info;
- }
- // 防止页面访问多次重定向
- @Bean
- public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator(){
-
- DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
- defaultAdvisorAutoProxyCreator.setUsePrefix(true);
-
- return defaultAdvisorAutoProxyCreator;
- }
- CREATE TABLE `permissions` (
- `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
- `name` VARCHAR(30) DEFAULT NULL COMMENT '权限名',
- `info` VARCHAR(30) DEFAULT NULL COMMENT '权限信息',
- `desc` VARCHAR(50) DEFAULT NULL COMMENT '描述',
- PRIMARY KEY (`id`)
- ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='权限表';

- CREATE TABLE `role_ps` (
- `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '编号',
- `rid` BIGINT(20) DEFAULT NULL COMMENT '角色 id',
- `pid` BIGINT(20) DEFAULT NULL COMMENT '权限 id',
- PRIMARY KEY (`id`)
- ) ENGINE=INNODB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COMMENT='角色权限映射表';

- SELECT info FROM permissions WHERE id IN (SELECT pid FROM role_ps WHERE rid
- IN (SELECT id FROM role WHERE NAME IN('admin','userMag')));
- @Select({
- ""
- })
- List
getUserPermissionInfoMapper(@Param("roles")List roles) ;
- //获取用户角色的权限信息
- @Override
- public List
getUserPermissionInfo(List roles) { - return userMapper.getUserPermissionInfoMapper(roles);
- }
- //自定义授权方法:获取当前登录用户权限信息,返回给 Shiro 用来进行授权对比
- @Override
- protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
- System.out.println("进入自定义授权方法");
- //获取当前用户身份信息
- String principal = principalCollection.getPrimaryPrincipal().toString();
- //调用接口方法获取用户的角色信息
- List
roles = userService.getUserRoleInfo(principal); - System.out.println("当前用户角色信息:"+roles);
- //调用接口方法获取用户角色的权限信息
- List
permissions = - userService.getUserPermissionInfo(roles);
- System.out.println("当前用户权限信息:"+permissions);
- //创建对象,存储当前登录的用户的权限和角色
- SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
- //存储角色
- info.addRoles(roles);
- //存储权限信息
- info.addStringPermissions(permissions);
- //返回
- return info;
- }
- //登录认证验证权限
- @RequiresPermissions("user:delete")
- @GetMapping("userPermissions")
- @ResponseBody
- public String userLoginPermissions() {
- System.out.println("登录认证验证权限");
- return "验证权限成功";
- }
Shiro 登录认证后主页面
- 登录用户为:"${session.user}">
- import org.apache.shiro.authz.AuthorizationException;
- import org.apache.shiro.authz.UnauthorizedException;
- import org.springframework.web.bind.annotation.ControllerAdvice;
- import org.springframework.web.bind.annotation.ExceptionHandler;
- import org.springframework.web.bind.annotation.ResponseBody;
-
- @ControllerAdvice
- public class PermissionsException {
- @ResponseBody
- @ExceptionHandler(UnauthorizedException.class)
- public String unauthorizedException(Exception ex){
- return "无权限";
- }
- @ResponseBody
- @ExceptionHandler(AuthorizationException.class)
- public String authorizationException(Exception ex){
- return "权限认证失败";
- }
- }
-
com.github.theborakompanioni -
thymeleaf-extras-shiro -
2.0.0
- @Bean
- public ShiroDialect shiroDialect(){
- return new ShiroDialect();
- }
|
guest
标签
|
|
用户没有身份验证时显示相应信息,即游客访问信息。
|
|
user
标签
|
|
用户已经身份验证
/
记住我登录后显示相应的信息。
|
|
authenticated
标签
|
|
用户已经身份验证通过,即
Subject.login
登录成功,不是记住我登录的。
|
|
notAuthenticated
标签
|
|
用户已经身份验证通过,即没有调用
Subject.login
进行登录,包括记住我自动登录的也属于未进行身份验证。
|
|
principal
标签
|
|
相当于
((User)Subject.getPrincipals()).getUsername()
。
|
|
lacksPermission
标签
|
|
如果当前
Subject
没有权限将显示
body
体内容。
|
|
hasRole
标签
|
|
如果当前
Subject
有角色将显示
body
体内容。
|
|
hasAnyRoles
标签
|
|
如果当前
Subject
有任意一个角色(或的关系)将显示
body
体内容。
|
|
lacksRole
标签
|
|
如果当前
Subject
没有角色将显示
body
体内容。
|
|
hasPermission
标签
|
|
如果当前
Subject
有权限将显示
body
体内容
|
Shiro 登录认证后主页面
- 登录用户为:"${session.user}">
- 授权-角色验证
- href="/myController/userPermissions">测试授权-权限验证

