SpringBoot + Shiro实战:实现Web应用的用户登录认证、Cookie自动登录和注销

一、后端

1.1 Shiro依赖

pom.xml

<dependency>
	<groupId>org.apache.shiro</groupId>
	<artifactId>shiro-spring</artifactId>
	<version>1.4.0-RC2</version>
</dependency>

1.2 登录实体类

用于存放登录的用户名、密码,并且回显员工姓名的实体类:
Employee.java

package com.jake.bpmmanager.pojo;

import lombok.Data;

@Data
public class Employee {

    private String account;

    private String password;

    private String familyName;

    private String firstName;
}

1.3 Shiro域

Realm:域,Shiro 从从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行操作;可以把 Realm 看成 DataSource,即安全数据源。
BPMShiroRealm.java

package com.jake.bpmmanager.realm;

import com.jake.bpmmanager.pojo.Employee;
import com.jake.bpmmanager.service.EmployeeService;
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.realm.SimpleAccountRealm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;

public class BPMShiroRealm extends SimpleAccountRealm {

    @Autowired
    private EmployeeService employeeService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String account = token.getPrincipal().toString();
        Employee emp = employeeService.getEmployeeByAccount(account);
        String empName = emp.getFamilyName() + emp.getFirstName();
        redisTemplate.opsForValue().set(account, empName);
        String password = emp.getPassword();
        if (password == null) {
            return null;
        }

        return new SimpleAuthenticationInfo(account, password, getName());

    }
}

1.4 Shiro配置

Shiro的核心配置类
ShiroConfig.java

package com.jake.bpmmanager.config;

import com.jake.bpmmanager.realm.BPMShiroRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setLoginUrl("/login.html");
        shiroFilterFactoryBean.setSuccessUrl("/index.html");

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/css/**", "anon");
        filterChainDefinitionMap.put("/authentication", "anon"); // 认证接口,最好位于根路径下。
        filterChainDefinitionMap.put("/logout", "logout"); // Shiro自带的注销接口
        filterChainDefinitionMap.put("/**", "user"); // 已经登录后,所有接口都能被当前用户访问。
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        Map<String, Filter> filterMap = new HashMap<>();
        filterMap.put("logout", logoutFilter());
        shiroFilterFactoryBean.setFilters(filterMap);

        return shiroFilterFactoryBean;
    }

    private LogoutFilter logoutFilter() {
        LogoutFilter logoutFilter = new LogoutFilter();
        logoutFilter.setRedirectUrl("/login.html");
        return  logoutFilter;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(BPMShiroRealm());
        securityManager.setRememberMeManager(cookieRememberMeManager());
        return securityManager;
    }

    @Bean
    public Realm BPMShiroRealm() {
        return new BPMShiroRealm();
    }

    @Bean
    public CookieRememberMeManager cookieRememberMeManager() {
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        return cookieRememberMeManager;
    }

    @Bean
    public SimpleCookie rememberMeCookie() {
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        simpleCookie.setMaxAge(3600);
        return simpleCookie;
    }

}

1.5 登录接口

authentication:用户名密码+是否自动登录的校验接口
visitor:index.html的访客姓名回显接口
LoginController.java

package com.jake.bpmmanager.controller;

import com.jake.bpmmanager.constant.URL;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
public class LoginController {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @GetMapping("authentication")
    public void authenticate(String account, String password, String rememberMe,
                             HttpServletResponse response) {
        Boolean isRemembered = StringUtils.isNotEmpty(rememberMe);
        UsernamePasswordToken token = new UsernamePasswordToken(account, password, isRemembered);
        Subject subject = SecurityUtils.getSubject();
        URL url = new URL();
        try {
            subject.login(token);
            url.setRedirectURL("");
        } catch (AuthenticationException e) {
            e.printStackTrace();
            url.setRedirectURL(URL.LOGIN_PAGE);
        } finally {
            try {
                response.sendRedirect(url.getRedirectURL());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

    @GetMapping("visitor")
    public String getEmployeeName() {
        Subject currentSubject = SecurityUtils.getSubject();
        if (currentSubject == null || currentSubject.getPrincipal() == null) {
            return "ERROR";
        }
        return redisTemplate.opsForValue().get(currentSubject.getPrincipal().toString());
    }

}

二、前端

2.1 登录页面

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>BPM后台管理系统登录页面</title>
    <link rel="stylesheet" type="text/css" href="css/login.css">
</head>
<body>
    <img src="css/images/bg.jpg" style="position:absolute;left:0;top:0;width:100%;height:100%;z-Index:-1">
    <div id="loginDiv">
        <h1>后台管理系统</h1>
        <form method="get" action="authentication">
            <p>
                <input type="text" name="account" id="account" placeholder="域账号">
            </p>
            <p>
                <input type="text" name="password" id="password" placeholder="您的生日,如19700101">
            </p><br>
            <p>
                记住我:<input id="rememberMe" type="checkbox" name="rememberMe" value="rememberMe">
                <input id="loginSubmit" type="submit" value="登录">
            </p>
        </form>
    </div>
</body>
</html>

2.2 注销

注:注销和用户名回显的HTML均包含在首页index.html中

<a href="#" onclick="window.confirm('确定注销吗?')?
    this.href='logout':this.href='javascript:void()';">注销</a>

2.3 用户名回显

HTML部分:

欢迎<span id="visitor"></span>

JS部分:

/**
 * 获取登录者姓名
 */
$.ajax({
	method: 'GET',
	cache: false,
	url: rootPath + "/visitor",
	success: function (name) {
		$('#visitor').html('<b>' + name + '</b>');
	}
});

发布了79 篇原创文章 · 获赞 322 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_15329947/article/details/88762581