springboot记录三,已成功集成shiro和mybatis

版权声明:本文为HCG原创文章,未经博主允许不得转载。请联系[email protected] https://blog.csdn.net/qq_39455116/article/details/83654629
1.引入maven
  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- freemarker -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
            <version>1.5.8.RELEASE</version>
        </dependency>

        <!-- 数据库 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!-- mybatis -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>

        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.8</version>
        </dependency>
        <!-- shiro -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
 
2.SQL脚本

在我的github地址里面有,整个项目和SQL

GitHub地址

3. sql解析

1. 一个用户有一个角色,用户表里面有role_id
2. login代表登陆时验证的用户,user_name作为中文名称,password是MD5加密后的密码
3. salt是盐,我这里的加密是经过5次加密算出来的密码,根据当前时间的出来的盐
4. 一个角色有N个权限,分别是sys_role_permission里面的一条记录
5. 权限表sys_permission只有permission_name parent_id 和permission_code有意义
 

4. 业务代码

开始写dao mapper service controller,具体的我不列了,可以看源码

5. Shiro相关配置

其实关于shiro的共有两个大的配置,一个是ShiroConfiguration一个是自定义的MyRealm

1. 先编写自定义的MyRealm

 package com.pf.org.cms.common;

import com.pf.org.cms.configuration.ShiroConfiguration;

import com.pf.org.cms.hcg.system.bean.PermissionDO;
import com.pf.org.cms.hcg.system.bean.UserDO;
import com.pf.org.cms.hcg.system.service.PermissionService;
import com.pf.org.cms.hcg.system.service.UserNewService;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Auther: pf
 * @Date: 2017/12/12 19:29
 * @Description: 认证和授权具体实现
 * 因为shiro并不知道你登陆认证的具体逻辑和授权的具体逻辑,所以需要用户自己实现,继承AuthorizingRealm,
 * 实现doGetAuthorizationInfo(授权)和doGetAuthenticationInfo(登陆认证)两个抽象方法
 */
public class MyRealm extends AuthorizingRealm {
    @Autowired
    private UserNewService userNewService;

    @Autowired
    private PermissionService permissionService;
    private static final Logger log = LoggerFactory.getLogger(MyRealm.class);

    /**
     * 为当前subject授权
     *
     * @param principalCollection
     * @return AuthorizationInfo
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("------------------授权");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        String loginName = (String) principalCollection.getPrimaryPrincipal();
        log.info("==========shiro 授权" + loginName);
        UserDO userDO = userNewService.getUserByLoginName(loginName);
        List<PermissionDO> permission = permissionService.findByUser(userDO);
        for (PermissionDO p : permission) {
            info.addStringPermission(p.getPermissionCode());
            log.info("===p.getPermissionCode()" + p.getPermissionCode());
        }
        return info;
    }

    /**
     * 认证登陆subject身份
     *
     * @param token
     * @return AuthenticationInfo
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        log.info("===============执行了认证方法");
        //认证只做了【查询数据库 用户是否存在】
        UserDO userDO = null;
        UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
        String loginName = usernamePasswordToken.getUsername();
        log.info("====UserRealm:token.getUsername():" + loginName);
        userDO = userNewService.getUserByLoginName(loginName);
        if (userDO == null) {
            throw new UnknownAccountException("用户不存在!");
        }
        Object principle = loginName;
        String credentials = userDO.getPassword();
        String realmName = getName();
        ByteSource salt = ByteSource.Util.bytes(userDO.getSalt());
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principle, credentials,salt,realmName);
        return info;

    }
    public static void main(String[] args) {    //====生成MD5加密后的: loginName+ salt.

        String hashAlgorithName = "MD5";
        String password = "wanwan";
        int hashIterations2 = 5;//加密次数
        ByteSource credentialsSalt = ByteSource.Util.bytes("1541123920660");
        Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations2);
        System.out.println(obj);


    }

}

 
5.2 编写ShiroConfiguration
package com.pf.org.cms.configuration;

import com.pf.org.cms.common.MyRealm;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import java.util.LinkedHashMap;

/**
 * @Auther: pf
 * @Date: 2017/12/12 19:34
 * @Description: shiro配置组件
 */
@Configuration
public class ShiroConfiguration {
    /**
     * 密码校验规则HashedCredentialsMatcher
     * 这个类是为了对密码进行编码的 ,
     * 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
     * 这个类也负责对form里输入的密码进行编码
     * 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
     */
    @Bean("hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //指定加密方式为MD5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //加密次数
        credentialsMatcher.setHashIterations(5);
        credentialsMatcher.setStoredCredentialsHexEncoded(true);
        return credentialsMatcher;
    }


    @Bean("MyRealm")
    @DependsOn("lifecycleBeanPostProcessor")//可选
    public MyRealm MyRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher matcher) {
        MyRealm MyRealm = new MyRealm();
        MyRealm.setAuthorizationCachingEnabled(false);
        MyRealm.setCredentialsMatcher(matcher);
        return MyRealm;
    }


    /**
     * 定义安全管理器securityManager,注入自定义的realm
     *
     * @param MyRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(@Qualifier("MyRealm") MyRealm MyRealm) {
        DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
        manager.setRealm(MyRealm);
        return manager;
    }


    /**
     * 定义shiroFilter过滤器并注入securityManager
     *
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        // 配置登录的url
        filterFactoryBean.setLoginUrl("/authenticate");
        //登录成功的url
        filterFactoryBean.setSuccessUrl("/home");
        // 配置未授权跳转页面
        filterFactoryBean.setUnauthorizedUrl("/errorPage/403");
        // 配置访问权限
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/css/**", "anon"); // 表示可以匿名访问
        filterChainDefinitionMap.put("/fonts/**", "anon");
        filterChainDefinitionMap.put("/imgs/**", "anon");
        filterChainDefinitionMap.put("/js/**", "anon");
        filterChainDefinitionMap.put("/auth/**", "anon");
        filterChainDefinitionMap.put("/errorPage/**", "anon");
        filterChainDefinitionMap.put("/demo/**", "anon");
        filterChainDefinitionMap.put("/swagger-*/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html/**", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/v2/**", "anon");

        // 表示admin权限才可以访问
        filterChainDefinitionMap.put("/admin/**", "roles[admin]");

        // 表示需要认证才可以访问
        filterChainDefinitionMap.put("/*", "authc");
        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("/*.*", "authc");
        filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return filterFactoryBean;
    }

    /**
     * Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理 .
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    /**
     * 配置shiro跟spring的关联
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    /**
     * lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
     * (可选)
     */
    @Bean("lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

}
 

6. controller测试

注意:我刚才在ShiroConfiguration里面设置的拦截url是authenticate

 // 配置登录的url
        filterFactoryBean.setLoginUrl("/authenticate");

所以controller模拟的时候用这个路径测试就行了

@Controller
public class UserController {
    @Autowired
    UserNewService userNewService;

    @Autowired
    PermissionService permissionService;


    private static final Logger log = LoggerFactory.getLogger(UserController.class);


    //用户用户名和密码MD5加密后的验证
    @RequestMapping(value = "authenticate", method = RequestMethod.GET)
    @ResponseBody
    public String authenticate(@RequestParam("loginName") String loginName, @RequestParam("password") String password,
                               HttpServletRequest request, HttpSession session, HttpServletResponse response) {

        //把前端输入的username和password封装为token
        UsernamePasswordToken token = new UsernamePasswordToken(loginName, password);
        // 认证身份
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
            session.setAttribute("user", subject.getPrincipal());
            log.info("登陆成功");
            return "success";
        } catch (Exception e) {
            log.info("登陆失败");
            return "false";
        }


    }



}

7. Postman测试

模拟MD5加密在数据库中添加数据

public static void main(String[] args) { 
//====生成MD5加密后的: loginName+ salt.
        String salt = Long.toString(System.currentTimeMillis());
        System.out.println("保存在数据库中的盐"+salt);
        ByteSource byteSource = ByteSource.Util.bytes(salt);
        String hashAlgorithName = "MD5";
        String password = "wanwan";
        int hashIterations2 = 5;//加密次数
        Object obj = new SimpleHash
        	(hashAlgorithName, password, byteSource, hashIterations2);
        System.out.println("保存在数据库中的密码:"+obj.toString());


    }

在用户表里面插入一条数据值分别是:上面生成的login_name 和password和salt即可

用下面这个URL直接在浏览器中测试即可

http://localhost:8080/authenticate?loginName=wanwan&password=wanwan

GitHub地址
























猜你喜欢

转载自blog.csdn.net/qq_39455116/article/details/83654629