ソースを参照

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

Junling Bu 7 年 前
コミット
78a062e234

+ 5 - 0
litemall-admin-api/pom.xml

@@ -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>

+ 6 - 9
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/annotation/support/LoginAdminHandlerMethodArgumentResolver.java

@@ -1,7 +1,9 @@
 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();
     }
 }

+ 76 - 0
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/config/ShiroConfig.java

@@ -0,0 +1,76 @@
+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;
+    }
+}

+ 0 - 42
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/dao/AdminToken.java

@@ -1,42 +0,0 @@
-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;
-    }
-}

+ 0 - 58
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/service/AdminTokenManager.java

@@ -1,58 +0,0 @@
-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;
-    }
-}

+ 69 - 0
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/ShiroAdminRealm.java

@@ -0,0 +1,69 @@
+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());
+    }
+
+}

+ 29 - 0
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/shiro/ShiroWebSessionManager.java

@@ -0,0 +1,29 @@
+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);
+         }
+    }
+}

+ 0 - 24
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAdminController.java

@@ -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,

+ 55 - 24
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java

@@ -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();
+    }
 }

+ 42 - 0
litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminIndexController.java

@@ -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");
+    }
 
 }

+ 3 - 3
litemall-admin/src/api/login.js

@@ -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 }
   })

+ 5 - 0
litemall-core/pom.xml

@@ -44,6 +44,11 @@
         </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>
             <exclusions>

+ 9 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/config/GlobalExceptionHandler.java

@@ -1,5 +1,7 @@
 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) {

+ 4 - 0
litemall-core/src/main/java/org/linlinjava/litemall/core/util/ResponseUtil.java

@@ -101,5 +101,9 @@ public class ResponseUtil {
     public static Object updatedDataFailed() {
         return fail(505, "更新数据失败");
     }
+
+    public static Object unauthz() {
+        return fail(506, "无操作权限");
+    }
 }
 

+ 6 - 0
pom.xml

@@ -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>