一、后端
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>');
}
});