springboot整合shiro

#Shiro
shiro主要是对用户登录、用户角色以及用户对应的权限进行认证管理
本文主要分为单个realm和多个realm进行认证授权管理测试demo
直接上代码多的也没有意义,我这边是结合springboot为例的

##单个realm
代码格式布局

在这里插入图片描述

###pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>shiro_demo</artifactId>
        <groupId>com.lix</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <modelVersion>4.0.0</modelVersion>

    <artifactId>shiro_springboot</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-web-starter</artifactId>
            <version>1.5.3</version>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3.3</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.24</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.5.1</version>
        </dependency>
    </dependencies>
    
</project>

###property文件

#shiro
shiro.loginUrl=/myController/login

# properties
server.port=8081
spring.thymeleaf.cache=false
spring.datasource.name=shirodb
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/shiro?useUnicode=true&serverTimezone=Asia/Shanghai&characterEncoding=utf8&autoReconnect=true&useSSL=false&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root

# mybatis config
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis.mapper-locations=classpath:mapper/*Mapper.xml

###realm
这个文件主要是根据账号查询出用户的身份信息、角色以及权限信息封装返回由底层进行比较

package com.lix.shiro.realm;

import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.service.UserService;
import lombok.extern.slf4j.Slf4j;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;

@Slf4j
@Component
public class MyRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    // 自定义授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("MyRealm-固定用户角色权限测试");
        log.info("自定义授权方法-查询用户的角色以及对应拥有的权限");
        String username = principalCollection.getPrimaryPrincipal().toString();
        // 查询用户角色
        List<UserRole> userRole = userService.getUserRole(username);
        HashSet<String> set = new HashSet<>();
        for(UserRole userRoleInfo:userRole){
            String roleName = userRoleInfo.getRoleName();
            set.add(roleName);
        }
        log.info("用户["+username+"]拥有的角色为:"+set);
        // 查询用户权限
        List<String> userPermissions = userService.getUserPermission(username);

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(set);
        info.addStringPermissions(userPermissions);
        return info;
    }

    // 自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("MyRealm--验证身份信息");
        log.info("自定义登录认证方法--查询用户的身份信息");
        // 获取用户身份信息
        String username = authenticationToken.getPrincipal().toString();
        String credentials = new String((char[])authenticationToken.getCredentials());
        // 获取持久化的用户信息
        User user = userService.getUserInfoByName(username);
        // 身份信息判断
        if(user==null){
            log.info("系统不存在该用户");
            return null;
        }
        // 封装身份信息返回认证
        AuthenticationInfo info = new SimpleAuthenticationInfo(
                username,
                user.getPassword(),
                ByteSource.Util.bytes(username),
                authenticationToken.getPrincipal().toString()
        );
        log.info("系统用户认证信息:"+username+"==="+credentials);
        return info;
    }
}

###shiroConfig
这个类主要功能就是shiro对请求进行拦截,规定需要通过身份认证才可以访问对应的页面或者具有相对的功能,以及还规定了认证需要的策略、加密方式、加密次数

package com.lix.shiro.config;

import com.lix.shiro.realm.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
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.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class ShiroConfig {

    @Resource
    private MyRealm myRealm;

    /**
     * 配置SecurityManager
     *
     * 认证策略
     * AtLeastOneSuccessfulStrategy--一个或多个Realm验证成功即可(默认认证策略)
     * FirstSuccessfulStrategy--第一个Realm验证成功 后续Realm忽略 视为成功
     * AllSuccessfulStrategy--所有Realm成功 认证视为成功
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        // 1.创建defaultWebSecurityManager对象
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

        // 2.创建加密对象,设置相关属性
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        //matcher.setHashSalted(true);
        // matcher.setHashIterations(3);

        // 3.将加密对戏存储到myRealm中
        myRealm.setCredentialsMatcher(matcher);
        // 4.将myRealm存入创建defaultWebSecurityManager对象
        defaultWebSecurityManager.setRealm(myRealm);

        // 5.设置rememberme
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager());

        // 6.返回
        return  defaultWebSecurityManager;
    }

    // 配置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");
        definition.addPathDefinition("/**","user");

        return  definition;
    }

    // 创建shiro的cookie管理对象
    private RememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
        return cookieRememberMeManager;
    }

    // cookie属性设置
    private Cookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        // 设置跨域
        //cookie.setDomain(domain);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30*24*60*60);
        return cookie;
    }
}

###controller

package com.lix.shiro.controller;

import com.lix.shiro.entity.User;
import lombok.extern.slf4j.Slf4j;
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.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("myController")
@Slf4j
public class UserController {

    @GetMapping("login")
    public String login() {
        return "login";
    }

    @GetMapping("userLogin")
    public String userLogin(User user, HttpSession session,
                            @RequestParam(defaultValue = "false")Boolean rememberMe){
        log.info("是否记住密码="+rememberMe);
        // 1.获取subject对象
        Subject subject = SecurityUtils.getSubject();
        // 2.封装数据到token
        AuthenticationToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword(),rememberMe);
        // 3.调用login方法进行认证
        try {
            subject.login(token);
            session.setAttribute("user",token.getPrincipal().toString());
            log.info("登录成功!");

        }catch (AuthenticationException e){
            log.info("登录失败!");
            e.printStackTrace();
        }
        return "main";
    }

    @GetMapping("userLoginRm")
    public String userLoginRm(HttpSession session){
        Subject subject = SecurityUtils.getSubject();
        String s = subject.getPrincipal().toString();
        log.info("===记住账号密码验证测试===");
        log.info("subject账号信息:"+s);
        return "main";
    }

    @RequiresRoles("role:admin")
    @GetMapping("userLoginRole")
    @ResponseBody
    public String userLoginRole(){
        log.info("测试角色认证成功");
        return "测试角色认证成功";
    }

    @RequiresPermissions("user:add")
    @GetMapping("userLoginPermissions")
    @ResponseBody
    public String userLoginPermissions(){
        log.info("测试权限认证成功");
        return "测试权限认证成功";
    }
}

###service

package com.lix.shiro.service;

import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;

import java.util.List;

public interface UserService {
    // 用户登录
    User getUserInfoByName(String username);

    // 角色查询
    List<UserRole> getUserRole(String username);

    // 权限查询
    List<String> getUserPermission(String username);
}

###serviceImpl

package com.lix.shiro.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.mapper.UserMapper;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

@Service
public class UserServiceImpl implements UserService{

    @Resource
    private UserMapper userMapper;

    @Override
    public User getUserInfoByName(String username) {
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username",username);
        User user = userMapper.selectOne(queryWrapper);
        return user;
    }

    @Override
    public List<UserRole> getUserRole(String username) {
        List<UserRole> list = userMapper.rolesQryByName(username);
        return list;
    }

    @Override
    public List<String> getUserPermission(String username) {
        List<String> strings = userMapper.permissionQryByName(username);
        return strings;
    }
}

###dao

package com.lix.shiro.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserMapper extends BaseMapper<User> {

    public List<UserRole> rolesQryByName(String name);

    public List<String> permissionQryByName(String name);
}

###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.lix.shiro.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.lix.shiro.entity.UserRole">
        <id column="id" jdbcType="BIGINT" property="Id"/>
        <result column="userName" jdbcType="VARCHAR" property="userName"/>
        <result column="roleName" jdbcType="VARCHAR" property="roleName"/>
    </resultMap>


    <select id="rolesQryByName" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        distinct a.id id ,a.username userName,c.name rolename
        from sys_user a
        left join sys_user_role b on a.id=b.user_id
        left join sys_role c on b.role_id=c.id
        where a.username = #{username}
    </select>

    <select id="permissionQryByName" parameterType="java.lang.String" resultType="java.lang.String">
        select
        distinct f.permission permission
        from sys_user a
        left join sys_user_role b on a.id=b.user_id
        left join sys_role c on b.role_id=c.id
        left join sys_role_resources d on c.id=d.role_id
		left join sys_resources f on d.resources_id=f.id
        where a.username = #{username} and f.permission is not null and f.permission !=''
    </select>

</mapper>

###entity实体类

package com.lix.shiro.entity;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_user")
public class User {
    private Integer id;
    private String username;
    private String password;
}

package com.lix.shiro.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserRole {
    private Integer Id;
    private String userName;
    private String roleName;
}

###html
login.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xml:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Shiro登录认证页面</h1>
<br>
<form action="/myController/userLogin">
    <div>用户名:<input type="text" name="username" value=""></div>
    <div>密码:<input type="password" name="password" value=""></div>
    <div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
    <div><input type="submit"  value="登录"></div>
</form>
</body>
</html>

main.html

<!DOCTYPE html>
<html lang="en" xml:th="http://www.thymeleaf.org" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Shiro登录认证成功后主页面</h1>
<br>
登录用户名为:<span th:text="${session.user}"></span>
<br>
<a href="/logout">登出</a>
<br>
<a href="/myController/userLoginRole">测试-角色</a>
<br>
<a href="/myController/userLoginPermissions">测试-权限</a>
</body>
</html>

##多个realms
代码结构
在这里插入图片描述

只添加改动或者新增的地方

###多个realms

package com.lix.shiro.realm;

import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;


@Slf4j
@Component
public class EmailRealm extends AuthorizingRealm {

    // 自定义授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    // 自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("EmailRealm--验证身份信息");
        // 获取用户身份信息
        String email = authenticationToken.getPrincipal().toString();
        String password = new String((char[])authenticationToken.getCredentials());
        // 根据邮箱获取用户信息-模拟就是用户前台输入的邮箱密码
        log.info("EmailRealm:"+email+"==="+password);

        // 封装身份信息返回认证
        AuthenticationInfo info = new SimpleAuthenticationInfo(
                email,
                password,
                authenticationToken.getPrincipal().toString()
        );
        return info;
    }
}

package com.lix.shiro.realm;

import com.lix.shiro.entity.User;
import com.lix.shiro.entity.UserRole;
import com.lix.shiro.service.UserService;
import lombok.extern.slf4j.Slf4j;
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.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.HashSet;
import java.util.List;

@Slf4j
@Component
public class NameRealm extends AuthorizingRealm {

    @Resource
    private UserService userService;

    // 自定义授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("MyRealm-固定用户角色权限测试");
        log.info("自定义授权方法-查询用户的角色以及对应拥有的权限");
        String username = principalCollection.getPrimaryPrincipal().toString();
        // 查询用户角色
        List<UserRole> userRole = userService.getUserRole(username);
        HashSet<String> set = new HashSet<>();
        for(UserRole userRoleInfo:userRole){
            String roleName = userRoleInfo.getRoleName();
            set.add(roleName);
        }
        log.info("用户["+username+"]拥有的角色为:"+set);
        // 查询用户权限
        List<String> userPermissions = userService.getUserPermission(username);

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.addRoles(set);
        info.addStringPermissions(userPermissions);
        return info;
    }

    // 自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("MyRealm--验证身份信息");
        log.info("自定义登录认证方法--查询用户的身份信息");
        // 获取用户身份信息
        String username = authenticationToken.getPrincipal().toString();
        String credentials = new String((char[])authenticationToken.getCredentials());
        // 获取持久化的用户信息
        User user = userService.getUserInfoByName(username);
        // 身份信息判断
        if(user==null){
            log.info("系统不存在该用户");
            return null;
        }
        // 封装身份信息返回认证
        AuthenticationInfo info = new SimpleAuthenticationInfo(
                username,
                user.getPassword(),
                ByteSource.Util.bytes(username),
                authenticationToken.getPrincipal().toString()
        );
        log.info("系统用户认证信息:"+username+"==="+credentials);
        return info;
    }
}

package com.lix.shiro.realm;

import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;

@Slf4j
@Component
public class PhoneRealm extends AuthorizingRealm {

    // 自定义授权方法
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        return null;
    }

    // 自定义登录认证方法
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        log.info("PhoneRealm--验证身份信息");
        // 获取用户身份信息
        String phone = authenticationToken.getPrincipal().toString();
        String password = new String((char[])authenticationToken.getCredentials());
        // 根据手机号获取用户信息-模拟就是用户前台输入的手机号密码
        log.info("PhoneRealm系统用户认证信息:"+phone+"==="+password);

        // 封装身份信息返回认证
        AuthenticationInfo info = new SimpleAuthenticationInfo(
                phone,
                password,
                authenticationToken.getPrincipal().toString()
        );
        return info;
    }
}

###config
这个方法是重写的 主要就是告诉shiro需要调用哪些realm

package com.lix.shiro.config;

import java.util.ArrayList;
import java.util.Collection;

import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 自定义身份认证realm控制器
 *
 * @ClassName: MyModularRealmAuthenticator
 * @Description 用于告诉shiro使用哪个realm处理
 */
public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
    private static final Logger log = LoggerFactory.getLogger(ModularRealmAuthenticator.class);

    @Override
    protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken)
            throws AuthenticationException {
        // 判断getRealms()是否返回为空
        assertRealmsConfigured();
        // 强制转换回自定义的CustomizedToken
        MyUsernamePasswordToken userToken = (MyUsernamePasswordToken) authenticationToken;
        // 登录类型
        String loginType = userToken.getLoginType();
        // 所有Realm
        Collection<Realm> realms = getRealms();
        // 登录类型对应的所有Realm
        Collection<Realm> typeRealms = new ArrayList<>();
        for (Realm realm : realms) {
            if (realm.getName().contains(loginType)) {
                typeRealms.add(realm);
            }
        }

        // 判断是单Realm还是多Realm
        if (typeRealms.size() == 1) {
            //单realm处理
            return doSingleRealmAuthentication(((ArrayList<Realm>) typeRealms).get(0), userToken);
        } else {
            //多realm处理,需满足全部realm认证
            return doMultiRealmAuthentication(typeRealms, userToken);
        }
    }

    /**
     * 重写doMultiRealmAuthentication,修复多realm联合认证只出现AuthenticationException异常,而未处理其他异常
     */
    protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) throws AuthenticationException {

        AuthenticationStrategy strategy = getAuthenticationStrategy();

        AuthenticationInfo aggregate = strategy.beforeAllAttempts(realms, token);

        if (log.isTraceEnabled()) {
            log.trace("Iterating through {} realms for PAM authentication", realms.size());
        }

        for (Realm realm : realms) {

            aggregate = strategy.beforeAttempt(realm, token, aggregate);

            if (realm.supports(token)) {

                log.trace("Attempting to authenticate token [{}] using realm [{}]", token, realm);

                AuthenticationInfo info = null;
                Throwable t = null;
                try {
                    info = realm.getAuthenticationInfo(token);
                } catch (Throwable throwable) {
                    t = throwable;
                    if (log.isDebugEnabled()) {
                        String msg = "Realm [" + realm + "] threw an exception during a multi-realm authentication attempt:";
                        log.debug(msg, t);
                    }
                }
                //处理只提示AuthenticationException异常问题
                if(t instanceof AuthenticationException) {
                    log.debug("realmName:"+realm.getName(), t);
                    throw ((AuthenticationException)t);
                }
                aggregate = strategy.afterAttempt(realm, token, info, aggregate, t);

            } else {
                log.debug("Realm [{}] does not support token {}.  Skipping realm.", realm, token);
            }
        }

        aggregate = strategy.afterAllAttempts(token, aggregate);

        return aggregate;
    }
}

package com.lix.shiro.config;

import org.apache.shiro.authc.UsernamePasswordToken;

/**
 *
 * @ClassName: MyUsernamePasswordToken
 * @Description 自定义用户名密码凭证类,携带登录类型信息
 * @version
 *
 */
public class MyUsernamePasswordToken extends UsernamePasswordToken {
    private static final long serialVersionUID = 1L;
    //登录类型
    private String loginType;

    public MyUsernamePasswordToken(String username, final String password, String loginType) {
        super(username, password);
        this.loginType = loginType;
    }

    public String getLoginType() {
        return loginType;
    }

    public void setLoginType(String loginType) {
        this.loginType = loginType;
    }

}

package com.lix.shiro.config;

import com.lix.shiro.realm.EmailRealm;
import com.lix.shiro.realm.NameRealm;
import com.lix.shiro.realm.PhoneRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AllSuccessfulStrategy;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.RememberMeManager;
import org.apache.shiro.realm.Realm;
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.Cookie;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

@Configuration
public class ShiroConfig {

    @Resource
    private NameRealm nameRealm;

    @Resource
    private PhoneRealm poneRealm;

    @Resource
    private EmailRealm emailRealm;

    /**
     * 配置SecurityManager
     *
     * 认证策略
     * AtLeastOneSuccessfulStrategy--一个或多个Realm验证成功即可(默认认证策略)
     * FirstSuccessfulStrategy--第一个Realm验证成功 后续Realm忽略 视为成功
     * AllSuccessfulStrategy--所有Realm成功 认证视为成功
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(){
        // 1.创建defaultWebSecurityManager对象
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

        // 2.创建加密对象,设置相关属性
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
        matcher.setHashAlgorithmName("md5");
        //matcher.setHashSalted(true);
        // matcher.setHashIterations(3);

        // 3.将加密对戏存储到nameRealm中
        nameRealm.setCredentialsMatcher(matcher);
        // 4.将nameRealm存入创建defaultWebSecurityManager对象
        defaultWebSecurityManager.setRealm(nameRealm);

        //===========================================================================================
        // 测试多个Realm认证
        // 创建认证对象,并设置认证策略
        // MyModularRealmAuthenticator-自己重写的需要认证对象
        MyModularRealmAuthenticator modularRealmAuthenticator = new MyModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator);
        List<Realm> list = new ArrayList<>();
        list.add(nameRealm);
        list.add(poneRealm);
        list.add(emailRealm);
        defaultWebSecurityManager.setRealms(list);
        //===========================================================================================

        // 5.设置rememberme
        defaultWebSecurityManager.setRememberMeManager(rememberMeManager());

        // 6.返回
        return  defaultWebSecurityManager;
    }

    // 配置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");
        definition.addPathDefinition("/**","user");

        return  definition;
    }

    // 创建shiro的cookie管理对象
    private RememberMeManager rememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        cookieRememberMeManager.setCipherKey("1234567890987654".getBytes());
        return cookieRememberMeManager;
    }

    // cookie属性设置
    private Cookie rememberMeCookie() {
        SimpleCookie cookie = new SimpleCookie("rememberMe");
        // 设置跨域
        //cookie.setDomain(domain);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge(30*24*60*60);
        return cookie;
    }
}

###controller

package com.lix.shiro.controller;

import com.lix.shiro.config.MyUsernamePasswordToken;
import com.lix.shiro.entity.User;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.authz.annotation.RequiresRoles;
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.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("myController")
@Slf4j
public class UserController {

    @GetMapping("login")
    public String login() {
        return "login";
    }

    @GetMapping("userLogin")
    public String userLogin(User user,
                            String loginType,
                            HttpSession session,
                            @RequestParam(defaultValue = "false")Boolean rememberMe){

        log.info("是否记住密码="+rememberMe);
        // 1.获取subject对象
        Subject subject = SecurityUtils.getSubject();
        // 2.封装数据到token
        // AuthenticationToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword(),rememberMe);
        MyUsernamePasswordToken token = new MyUsernamePasswordToken(user.getUsername(), user.getPassword(), loginType);
        // 3.调用login方法进行认证
        try {
            subject.login(token);
            session.setAttribute("user",token.getPrincipal().toString());
            log.info("登录成功!");

        }catch (UnknownAccountException uae) {
            // 用户名未知...
            log.info("用户不存在!");
        } catch (IncorrectCredentialsException ice) {
            // 凭据不正确,例如密码不正确 ...
            log.info("密码不正确!");
        } catch (LockedAccountException lae) {
            // 用户被锁定,例如管理员把某个用户禁用...
            log.info("用户被锁定!");
        } catch (ExcessiveAttemptsException eae) {
            // 尝试认证次数多余系统指定次数 ...
            log.info("尝试认证次数过多,请稍后重试!");
        } catch (AuthenticationException ae) {
            // 其他未指定异常
            log.info("未知异常!");
        }
        return "main";
    }

    @GetMapping("userLoginRm")
    public String userLoginRm(HttpSession session){
        Subject subject = SecurityUtils.getSubject();
        String s = subject.getPrincipal().toString();
        log.info("===记住账号密码验证测试===");
        log.info("subject账号信息:"+s);
        return "main";
    }

    @RequiresRoles("role:admin")
    @GetMapping("userLoginRole")
    @ResponseBody
    public String userLoginRole(){
        log.info("测试角色认证成功");
        return "测试角色认证成功";
    }

    @RequiresPermissions("user:add")
    @GetMapping("userLoginPermissions")
    @ResponseBody
    public String userLoginPermissions(){
        log.info("测试权限认证成功");
        return "测试权限认证成功";
    }
}

###html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
        "http://www.w3.org/TR/html4/loose.dtd">
<html lang="en" xml:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>Shiro登录认证页面</h1>
<br>
<form action="/myController/userLogin">
    <div>账号:<input type="text" name="username" value=""></div>
    <div>密码:<input type="password" name="password" value=""></div>
    <table>
        <td>用户类型</td>
        <td>
            <select name="loginType" id="loginType">
                <option value="Name">用户名登录</option>
                <option value="Phone">手机号登录</option>
                <option value="Email">邮箱登录</option>
            </select>
        </td>
    </table>
    <div>记住用户:<input type="checkbox" name="rememberMe" value="true"></div>
    <div><input type="submit"  value="登录"></div>
</form>
</body>
</html>

完整代码跳转地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值