Commit 78a062e2 authored by Junling Bu's avatar Junling Bu
Browse files

feat[litemall-admin, litemall-admin-api, litemall-core]:引入shiro库,代替原来的登陆认证机制,但是还不支持授权。

parent 7f8fafb9
......@@ -26,6 +26,11 @@
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
</dependency>
</dependencies>
<build>
......
package org.linlinjava.litemall.admin.annotation.support;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.service.AdminTokenManager;
import org.linlinjava.litemall.db.domain.LitemallAdmin;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
......@@ -10,7 +12,6 @@ import org.springframework.web.method.support.ModelAndViewContainer;
public class LoginAdminHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
public static final String LOGIN_TOKEN_KEY = "X-Litemall-Admin-Token";
@Override
public boolean supportsParameter(MethodParameter parameter) {
......@@ -20,13 +21,9 @@ public class LoginAdminHandlerMethodArgumentResolver implements HandlerMethodArg
@Override
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
Subject currentUser = SecurityUtils.getSubject();
LitemallAdmin admin = (LitemallAdmin) currentUser.getPrincipal();
// return new Integer(1);
String token = request.getHeader(LOGIN_TOKEN_KEY);
if (token == null || token.isEmpty()) {
return null;
}
return AdminTokenManager.getUserId(token);
return admin.getId();
}
}
package org.linlinjava.litemall.admin.config;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.linlinjava.litemall.admin.shiro.ShiroAdminRealm;
import org.linlinjava.litemall.admin.shiro.ShiroWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new ShiroAdminRealm();
}
// @Bean
// public ShiroFilterChainDefinition shiroFilterChainDefinition() {
// DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
// chain.addPathDefinition("/admin/login/login", "anon");
// chain.addPathDefinition("/admin/login/unauth", "anon");
// chain.addPathDefinition("/admin/**", "authc");
// return chain;
// }
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
filterChainDefinitionMap.put("/admin/auth/login", "anon");
filterChainDefinitionMap.put("/admin/auth/401", "anon");
filterChainDefinitionMap.put("/admin/auth/index", "anon");
filterChainDefinitionMap.put("/admin/auth/403", "anon");
filterChainDefinitionMap.put("/admin/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/admin/auth/401");
shiroFilterFactoryBean.setSuccessUrl("/admin/auth/index");
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/auth/403");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
@Bean
public SessionManager sessionManager() {
ShiroWebSessionManager mySessionManager = new ShiroWebSessionManager();
return mySessionManager;
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm());
securityManager.setSessionManager(sessionManager());
return securityManager;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
package org.linlinjava.litemall.admin.dao;
import java.time.LocalDateTime;
public class AdminToken {
private Integer userId;
private String token;
private LocalDateTime expireTime;
private LocalDateTime updateTime;
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public LocalDateTime getExpireTime() {
return expireTime;
}
public void setExpireTime(LocalDateTime expireTime) {
this.expireTime = expireTime;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}
package org.linlinjava.litemall.admin.service;
import org.linlinjava.litemall.admin.dao.AdminToken;
import org.linlinjava.litemall.core.util.CharUtil;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
public class AdminTokenManager {
private static Map<String, AdminToken> tokenMap = new HashMap<>();
private static Map<Integer, AdminToken> idMap = new HashMap<>();
public static Integer getUserId(String token) {
AdminToken userToken = tokenMap.get(token);
if (userToken == null) {
return null;
}
if (userToken.getExpireTime().isBefore(LocalDateTime.now())) {
tokenMap.remove(token);
idMap.remove(userToken.getUserId());
return null;
}
return userToken.getUserId();
}
public static AdminToken generateToken(Integer id) {
AdminToken userToken = null;
// userToken = idMap.get(id);
// if(userToken != null) {
// tokenMap.remove(userToken.getToken());
// idMap.remove(id);
// }
String token = CharUtil.getRandomString(32);
while (tokenMap.containsKey(token)) {
token = CharUtil.getRandomString(32);
}
LocalDateTime update = LocalDateTime.now();
LocalDateTime expire = update.plusDays(1);
userToken = new AdminToken();
userToken.setToken(token);
userToken.setUpdateTime(update);
userToken.setExpireTime(expire);
userToken.setUserId(id);
tokenMap.put(token, userToken);
idMap.put(id, userToken);
return userToken;
}
}
package org.linlinjava.litemall.admin.shiro;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
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.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
import org.linlinjava.litemall.db.domain.LitemallAdmin;
import org.linlinjava.litemall.db.service.LitemallAdminService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.List;
public class ShiroAdminRealm extends AuthorizingRealm {
private static final Logger log = LoggerFactory.getLogger(ShiroAdminRealm.class);
@Autowired
private LitemallAdminService adminService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
if (principals == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addRole("admin");
info.addStringPermission("user");
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
String password=new String(upToken.getPassword());
if (StringUtils.isEmpty(username)) {
throw new AccountException("用户名不能为空");
}
if (StringUtils.isEmpty(password)) {
throw new AccountException("密码不能为空");
}
List<LitemallAdmin> adminList = adminService.findAdmin(username);
Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
if (adminList.size() == 0) {
throw new UnknownAccountException("找不到用户("+username+")的帐号信息");
}
LitemallAdmin admin = adminList.get(0);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(password, admin.getPassword())) {
throw new UnknownAccountException("找不到用户("+username+")的帐号信息");
}
return new SimpleAuthenticationInfo(admin,password,getName());
}
}
package org.linlinjava.litemall.admin.shiro;
import com.alibaba.druid.util.StringUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
public class ShiroWebSessionManager extends DefaultWebSessionManager {
public static final String LOGIN_TOKEN_KEY = "X-Litemall-Admin-Token";
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
String id = WebUtils.toHttp(request).getHeader(LOGIN_TOKEN_KEY);
if (!StringUtils.isEmpty(id)) {
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
} else {
return super.getSessionId(request, response);
}
}
}
......@@ -3,7 +3,6 @@ package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.service.AdminTokenManager;
import org.linlinjava.litemall.core.util.RegexUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
......@@ -33,29 +32,6 @@ public class AdminAdminController {
@Autowired
private LitemallAdminService adminService;
@GetMapping("/info")
public Object info(String token) {
Integer adminId = AdminTokenManager.getUserId(token);
if (adminId == null) {
return ResponseUtil.badArgumentValue();
}
LitemallAdmin admin = adminService.findById(adminId);
if (admin == null) {
return ResponseUtil.badArgumentValue();
}
Map<String, Object> data = new HashMap<>();
data.put("name", admin.getUsername());
data.put("avatar", admin.getAvatar());
// 目前roles不支持,这里简单设置admin
List<String> roles = new ArrayList<>();
roles.add("admin");
data.put("roles", roles);
data.put("introduction", "admin introduction");
return ResponseUtil.ok(data);
}
@GetMapping("/list")
public Object list(@LoginAdmin Integer adminId,
String username,
......
......@@ -2,29 +2,28 @@ package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.subject.Subject;
import org.linlinjava.litemall.admin.annotation.LoginAdmin;
import org.linlinjava.litemall.admin.dao.AdminToken;
import org.linlinjava.litemall.admin.service.AdminTokenManager;
import org.linlinjava.litemall.core.util.JacksonUtil;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.linlinjava.litemall.core.util.bcrypt.BCryptPasswordEncoder;
import org.linlinjava.litemall.db.domain.LitemallAdmin;
import org.linlinjava.litemall.db.service.LitemallAdminService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.linlinjava.litemall.admin.util.AdminResponseCode.ADMIN_INVALID_ACCOUNT;
@RestController
@RequestMapping("/admin/login")
@RequestMapping("/admin/auth")
@Validated
public class AdminAuthController {
private final Log logger = LogFactory.getLog(AdminAuthController.class);
......@@ -44,23 +43,18 @@ public class AdminAuthController {
return ResponseUtil.badArgument();
}
List<LitemallAdmin> adminList = adminService.findAdmin(username);
Assert.state(adminList.size() < 2, "同一个用户名存在两个账户");
if (adminList.size() == 0) {
return ResponseUtil.badArgumentValue();
}
LitemallAdmin admin = adminList.get(0);
Subject currentUser = SecurityUtils.getSubject();
try {
currentUser.login(new UsernamePasswordToken(username, password));
} catch (UnknownAccountException uae) {
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号或密码不正确");
} catch (LockedAccountException lae) {
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "用户帐号已锁定不可用");
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(password, admin.getPassword())) {
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, "账号密码不对");
} catch (AuthenticationException ae) {
return ResponseUtil.fail(ADMIN_INVALID_ACCOUNT, ae.getMessage());
}
Integer adminId = admin.getId();
// token
AdminToken adminToken = AdminTokenManager.generateToken(adminId);
return ResponseUtil.ok(adminToken.getToken());
return ResponseUtil.ok(currentUser.getSession().getId());
}
/*
......@@ -72,6 +66,43 @@ public class AdminAuthController {
return ResponseUtil.unlogin();
}
Subject currentUser = SecurityUtils.getSubject();
currentUser.logout();
return ResponseUtil.ok();
}
@GetMapping("/info")
public Object info(@LoginAdmin Integer adminId) {
LitemallAdmin admin = adminService.findById(adminId);
if (admin == null) {
return ResponseUtil.badArgumentValue();
}
Map<String, Object> data = new HashMap<>();
data.put("name", admin.getUsername());
data.put("avatar", admin.getAvatar());
// 目前roles不支持,这里简单设置admin
List<String> roles = new ArrayList<>();
roles.add("admin");
data.put("roles", roles);
data.put("introduction", "admin introduction");
return ResponseUtil.ok(data);
}
@GetMapping("/401")
public Object page401() {
return ResponseUtil.unlogin();
}
@GetMapping("/index")
public Object pageIndex() {
return ResponseUtil.ok();
}
@GetMapping("/403")
public Object page403() {
return ResponseUtil.unauthz();
}
}
......@@ -2,6 +2,7 @@ package org.linlinjava.litemall.admin.web;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.authz.annotation.*;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
......@@ -16,5 +17,46 @@ public class AdminIndexController {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresGuest
@RequestMapping("/guest")
public Object guest() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresAuthentication
@RequestMapping("/authn")
public Object authn() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresUser
@RequestMapping("/user")
public Object user() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresRoles("admin")
@RequestMapping("/admin")
public Object admin() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresRoles("admin2")
@RequestMapping("/admin2")
public Object admin2() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresPermissions("index:permission:read")
@RequestMapping("/read")
public Object read() {
return ResponseUtil.ok("hello world, this is admin service");
}
@RequiresPermissions("index:permission:write")
@RequestMapping("/write")
public Object write() {
return ResponseUtil.ok("hello world, this is admin service");
}
}
......@@ -6,7 +6,7 @@ export function loginByUsername(username, password) {
password
}
return request({
url: '/login/login',
url: '/auth/login',
method: 'post',
data
})
......@@ -14,14 +14,14 @@ export function loginByUsername(username, password) {
export function logout() {
return request({
url: '/login/logout',
url: '/auth/logout',
method: 'post'
})
}
export function getUserInfo(token) {
return request({
url: '/admin/info',
url: '/auth/info',
method: 'get',
params: { token }
})
......
......@@ -43,6 +43,11 @@
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
</dependency>
<dependency>
<groupId>com.aliyun.oss</groupId>
<artifactId>aliyun-sdk-oss</artifactId>
......
package org.linlinjava.litemall.core.config;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.hibernate.validator.internal.engine.path.PathImpl;
import org.linlinjava.litemall.core.util.ResponseUtil;
import org.springframework.http.converter.HttpMessageNotReadableException;
......@@ -45,6 +47,13 @@ public class GlobalExceptionHandler {
return ResponseUtil.badArgumentValue();
}
@ExceptionHandler(AuthorizationException.class)
@ResponseBody
public Object unauthorizedHandler(AuthorizationException e) {
e.printStackTrace();
return ResponseUtil.unauthz();
}
@ExceptionHandler(ValidationException.class)
@ResponseBody
public Object badArgumentHandler(ValidationException e) {
......
......@@ -101,5 +101,9 @@ public class ResponseUtil {
public static Object updatedDataFailed() {
return fail(505, "更新数据失败");
}
public static Object unauthz() {
return fail(506, "无操作权限");
}
}
......@@ -69,6 +69,12 @@
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<!-- MySQL 连接驱动依赖 -->
<dependency>
<groupId>mysql</groupId>
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment