shiro登录验证+权限认证

此项目是idea+springboot+mybatis包含不少小知识,代码没粘全!如有需要百度网盘下载地址:

链接:https://pan.baidu.com/s/1kVsIzwca9KlygeRwSbc9cg 
提取码:fdza

也可以加Q:1016844010 不会的可以问我!

 

项目结构:

application.yml

spring:
  datasource:
    name : shiro
    url : jdbc:mysql://localhost:3306/shiro?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
    username : root
    password : 123456
    driver-class-name: com.mysql.jdbc.Driver
    filters: stat
    maxActive: 20
    initialSize: 1
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 'x'
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20


thymeleaf:
  prefix: classpath:/templates/
  content-type: text/html
  cache: false
  mode: LEGACYHTML5


# 驼峰标志属性与数据库字段自动属性映射
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true

pagehelper:
  helperDialect: mysql
  reasonable: true
  supportMethodsArguments: true
  returnPageInfo: check
  params:
    count: countSql

kaptcha.properties:

##################################################
kaptcha.border=no
kaptcha.textproducer.font.color=blue
kaptcha.image.width=110
kaptcha.image.height=60
kaptcha.session.key=code
kaptcha.textproducer.char.length=4
kaptcha.textproducer.font.size=5
kaptcha.background.clear.from=green
kaptcha.background.clear.to=white
kaptcha.textproducer.font.names=Arial,Courier

html页面:


    <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet">
    <link href="/css/login.css" rel="stylesheet">
    <script src="/js/jquery-1.12.4.min.js"></script>
    <script src="/js/bootstrap.min.js?v=3.3.6"></script>
    <script src="/js/aexit.js"></script>


<div class="middle-box text-center loginscreen  animated fadeInDown">
<div  class="cc">
    <div>
        <h1 class="logo-name"></h1>
    </div>
    <h4 id="tips" style="color: red;"></h4>
    <div class="form-group">
        <label class="name">用 户 名<span></span></label>
        <input type="text" id="username" name="username" class="form-control" placeholder="用户ID" onKeyPress="IsEnterKeyPress()" required>
    </div>
    <div class="form-group">
        <label class="name">密 码<span></span></label>
        <input type="password" id="password" name="password" class="form-control password" placeholder="密码" onKeyPress="IsEnterKeyPress()" required>
    </div>
    <div class="form-group">
        <label class="name">验 证 码<span></span></label>
        <div class="col-sm-8" style="padding-left: 0px; padding-right: 0px;">
            <input class="form-control  code" id="ckaptcha" type="text" name="kaptcha" placeholder="验证码" onKeyPress="IsEnterKeyPress()" required>
        </div>
        <div class="col-sm-4" style="padding-left: 0px; padding-right: 0px;">
            <img src="/kaptcha" id="kaptcha" width="80px" height="40px"/>
        </div>
    </div>

    <button type="submit" onclick="loginFn()" class="btn btn-primary block full-width m-b">登 录</button>

</div>
</div>


<script>
    $(function(){
        //点击更换验证码
        $("#kaptcha").on('click',function(){
            $("#kaptcha").attr('src', '/kaptcha?' + Math.floor(Math.random()*100) ).fadeIn();
        });

        document.body.style.backgroundImage="url(img/bg_img03.jpg)";
        $(".logo-name").css("background-image","url(img/logo3.png)");
    });

    // 按回车
    function IsEnterKeyPress(){
        var lKeyCode = (navigator.appname=="Netscape")?event.which:window.event.keyCode;
        if ( lKeyCode == 13 ){
            loginFn();
        }
    }

    //登陆动作
    function loginFn (){
        var username = $('#username').val();
        var password = $('#password').val();
        var userValidateCode = $('#ckaptcha').val();
        $.ajax({
            type: "POST",
            data: {
                "username":username,
                "password":password,
                "user_validate_code":userValidateCode
            },
            url: Aexit.ctxPath + "/doLogin",
            dataType: "json",
            cache: false,
            success: function(data){
                if(data.code === 0){
                    window.location.href = "/home";
                }else{
                    $("#tips").text(data.message);
                    $("#kaptcha").attr('src', '/kaptcha?' + Math.floor(Math.random()*100) ).fadeIn();
                }
            },error : function(XMLHttpRequest, textStatus, errorThrown) {
                $("#tips").val("网络或服务器出现问题,请稍后再试!");
            }
        });

    }

</script>

LoginController:

package com.example.weiyu.controller;

import com.example.weiyu.config.AjaxCommonObject;
import com.example.weiyu.config.BizCommonException;
import com.example.weiyu.config.ShiroKit;
import com.example.weiyu.entity.User;
import com.example.weiyu.service.UserService;
import com.example.weiyu.util.AuthConstants;
import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.impl.DefaultKaptcha;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.awt.image.BufferedImage;

@Controller
public class LoginController {
    @Autowired
    private UserService userService;
    @Resource
    private DefaultKaptcha captchaProducer;
    @RequestMapping(value = "index")
    public String index(){
        return "login";
    }

    @RequestMapping(value = "home")
    public String home(){
        return "layout/index";
    }

    /**
     * 登录验证
     */
    @RequestMapping(value="/doLogin",method = RequestMethod.POST)
    @ResponseBody
    public AjaxCommonObject doLogin(@RequestParam String username, @RequestParam String password,
                                    @RequestParam(value="user_validate_code",required = false) String userValidateCode,
                                    HttpSession session) {
        AjaxCommonObject ajaxCommonObject = new AjaxCommonObject();
        User user = userService.findUserByName(username.trim());

        if (!userValidateCode.equals(session.getAttribute(Constants.KAPTCHA_SESSION_KEY))) {
            return  new AjaxCommonObject(new BizCommonException(400,"请输入正确的验证码!"));
        }
        try {
            Subject subject = SecurityUtils.getSubject();

            UsernamePasswordToken token = new UsernamePasswordToken(username, ShiroKit.md5(password, user.getSalt()));


            subject.login(token);
        } catch (Exception e) {
            return new AjaxCommonObject(new BizCommonException(400,"请输入正确的用户名密码----!"));
        }
        session.setAttribute(AuthConstants.CURRENT_USER, user);
        return ajaxCommonObject;
    }


    /**
     * 获取验证码
     */
    @GetMapping(value = "/kaptcha")
    public void kaptcha(HttpSession session, HttpServletResponse response) throws Exception {

        try (ServletOutputStream out = response.getOutputStream()) {
            response.setDateHeader("Expires", 0);
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            response.addHeader("Cache-Control", "post-check=0, pre-check=0");
            response.setHeader("Pragma", "no-cache");
            response.setContentType("image/jpeg");

            String capText = captchaProducer.createText();

            //TODO 如果是前后台分离或分布式部署,这里需要使用SpringSession放到redis里面
            //将验证码存到session
            session.setAttribute(Constants.KAPTCHA_SESSION_KEY, capText);

            BufferedImage bi = captchaProducer.createImage(capText);
            ImageIO.write(bi, "jpg", out);

            out.flush();
        }
    }
}

pom.xml

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatisplus-spring-boot-starter.version>1.0.4</mybatisplus-spring-boot-starter.version>
        <mybatisplus.version>2.1-gamma</mybatisplus.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatisplus-spring-boot-starter</artifactId>
            <version>${mybatisplus-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>

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


        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf-spring3 -->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring3</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.3.2</version>
        </dependency>
        <!--添加验证码依赖-->
        <dependency>
            <groupId>com.github.penggle</groupId>
            <artifactId>kaptcha</artifactId>
            <version>2.3.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>javax.servlet-api</artifactId>
                    <groupId>javax.servlet</groupId>
                </exclusion>
            </exclusions>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <filtering>false</filtering>
                <includes>
                    <include>**/*.xml</include>
                </includes>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>
登录和权限认证EnceladusShiroRealm:
package com.example.weiyu.common;

import com.example.weiyu.entity.Menu;
import com.example.weiyu.entity.Role;
import com.example.weiyu.entity.User;
import com.example.weiyu.service.MenuService;
import com.example.weiyu.service.RoleService;
import com.example.weiyu.service.UserService;
import org.apache.shiro.authc.*;
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.springframework.beans.factory.annotation.Autowired;

import java.util.List;

public class EnceladusShiroRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private MenuService menuService;


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        //权限信息对象authorizationInfo,用来存放查出的用户的所有的角色(role)及权限(permission)
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        String username = (String) principals.getPrimaryPrincipal();

        User user = userService.findUserByName(username);
        List<Role> sysRole = roleService.selectRole(user.getId());
        for (Role role : sysRole) {
            authorizationInfo.addRole(role.getName());
            List<Menu> sysPermissions=menuService.selectPermission(role.getId());
            for (Menu permission : sysPermissions) {
                authorizationInfo.addStringPermission(permission.getName());
            }
        }
        return authorizationInfo;
    }
    /**
     * 配置登录认证信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
        String username = (String) token.getPrincipal();
        User user = userService.findUserByName(username);

        if (user == null) {
            return null;
        }

        SimpleAuthenticationInfo  authenticationInfo = new SimpleAuthenticationInfo (username,user.getPassword(),getName());
        return authenticationInfo;
    }

}
拦截器ShiroConfig:
package com.example.weiyu.common;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

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

        Map<String, String> filterChainDefinitionMap = new HashMap<String, String>();
        //配置记住我或认证通过可以访问的地址
        filterChainDefinitionMap.put("/index", "anon");
        filterChainDefinitionMap.put("/applogin", "anon");
        filterChainDefinitionMap.put("/home", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");//静态资源不拦截
        filterChainDefinitionMap.put("/doLogin", "anon");//anon 可以理解为不拦截
        /*filterChainDefinitionMap.put("/logout", "anon");//anon 可以理解为不拦截*/
        filterChainDefinitionMap.put("/kaptcha", "anon");//anon 可以理解为不拦截
        filterChainDefinitionMap.put("/", "anon");
       /* filterChainDefinitionMap.put("/**", "authc,myperm");*/





        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        /*shiroFilterFactoryBean.setLoginUrl("/");
        shiroFilterFactoryBean.setUnauthorizedUrl("/");*/

       /* URLPermissionsFilter urlPermissionsFilter = new URLPermissionsFilter();
        Map<String, Filter> filtermap = new HashMap<>();
        filtermap.put("myperm",urlPermissionsFilter);shiroFilterFactoryBean.setFilters(filtermap);*/
        return shiroFilterFactoryBean;
    }



    @Bean
    public EnceladusShiroRealm shiroRealm() {
        EnceladusShiroRealm shiroRealm = new EnceladusShiroRealm();
        return shiroRealm;
    }

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

}
验证码有关CaptchaConfig:
package com.example.weiyu.config;

import com.google.code.kaptcha.impl.DefaultKaptcha;
import com.google.code.kaptcha.util.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Properties;

@Component
@PropertySource(value = {"classpath:kaptcha.properties"})
public class CaptchaConfig {
    @Value("${kaptcha.border}")
    private String border;
    @Value("${kaptcha.textproducer.font.color}")
    private String fontColor;
    @Value("${kaptcha.image.width}")
    private String imageWidth;
    @Value("${kaptcha.image.height}")
    private String imageHeight;
    @Value("${kaptcha.session.key}")
    private String sessionKey;
    @Value("${kaptcha.textproducer.char.length}")
    private String charLength;
    @Value("${kaptcha.textproducer.font.names}")
    private String fontNames;

    @Bean(name = "captchaProducer")
    DefaultKaptcha getKaptchaBean() {
        DefaultKaptcha defaultKaptcha = new DefaultKaptcha();
        Properties properties = new Properties();
        properties.setProperty("kaptcha.border", border);
        properties.setProperty("kaptcha.textproducer.font.color", fontColor);
        properties.setProperty("kaptcha.image.width", imageWidth);
        properties.setProperty("kaptcha.image.height", imageHeight);
        properties.setProperty("kaptcha.session.key", sessionKey);
        properties.setProperty("kaptcha.textproducer.char.length", charLength);
        properties.setProperty("kaptcha.textproducer.font.names", fontNames);
        Config config = new Config(properties);
        defaultKaptcha.setConfig(config);
        return defaultKaptcha;
    }
}
密码盐ShiroKit:
package com.example.weiyu.config;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;

import java.util.Random;

public class ShiroKit {

    private static final String NAMES_DELIMETER = ",";

    /**
     * 加盐参数
     */
    public final static String hashAlgorithmName = "MD5";

    /**
     * 循环次数
     */
    public final static int hashIterations = 1024;

    /**
     * shiro密码加密工具类
     *
     * @param credentials 密码
     * @param saltSource 密码盐
     * @return
     */
    public static String md5(String credentials, String saltSource) {
        ByteSource salt = new Md5Hash(saltSource);
        return new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations).toString();
    }

    /**
     * 获取随机盐值
     * @param length
     * @return
     */
    public static String getRandomSalt(int length) {
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < length; i++) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }
        return sb.toString();
    }

    /**
     * 获取当前 Subject
     *
     * @return Subject
     */
    public static Subject getSubject() {
        return SecurityUtils.getSubject();
    }


    /**
     * 从shiro获取session
     *
     */
    public static Session getSession() {
        return getSubject().getSession();
    }

    /**
     * 获取shiro指定的sessionKey
     *
     */
    @SuppressWarnings("unchecked")
    public static <T> T getSessionAttr(String key) {
        Session session = getSession();
        return session != null ? (T) session.getAttribute(key) : null;
    }

    /**
     * 设置shiro指定的sessionKey
     *
     */
    public static void setSessionAttr(String key, Object value) {
        Session session = getSession();
        session.setAttribute(key, value);
    }

    /**
     * 移除shiro指定的sessionKey
     */
    public static void removeSessionAttr(String key) {
        Session session = getSession();
        if (session != null)
            session.removeAttribute(key);
    }

    /**
     * 验证当前用户是否属于该角色?,使用时与lacksRole 搭配使用
     *
     * @param roleName
     *            角色名
     * @return 属于该角色:true,否则false
     */
    public static boolean hasRole(String roleName) {
        return getSubject() != null && roleName != null
                && roleName.length() > 0 && getSubject().hasRole(roleName);
    }

    /**
     * 与hasRole标签逻辑相反,当用户不属于该角色时验证通过。
     *
     * @param roleName
     *            角色名
     * @return 不属于该角色:true,否则false
     */
    public static boolean lacksRole(String roleName) {
        return !hasRole(roleName);
    }

    /**
     * 验证当前用户是否属于以下任意一个角色。
     *
     * @param roleNames
     *            角色列表
     * @return 属于:true,否则false
     */
    public static boolean hasAnyRoles(String roleNames) {
        boolean hasAnyRole = false;
        Subject subject = getSubject();
        if (subject != null && roleNames != null && roleNames.length() > 0) {
            for (String role : roleNames.split(NAMES_DELIMETER)) {
                if (subject.hasRole(role.trim())) {
                    hasAnyRole = true;
                    break;
                }
            }
        }
        return hasAnyRole;
    }

    /**
     * 验证当前用户是否属于以下所有角色。
     *
     * @param roleNames
     *            角色列表
     * @return 属于:true,否则false
     */
    public static boolean hasAllRoles(String roleNames) {
        boolean hasAllRole = true;
        Subject subject = getSubject();
        if (subject != null && roleNames != null && roleNames.length() > 0) {
            for (String role : roleNames.split(NAMES_DELIMETER)) {
                if (!subject.hasRole(role.trim())) {
                    hasAllRole = false;
                    break;
                }
            }
        }
        return hasAllRole;
    }

    /**
     * 验证当前用户是否拥有指定权限,使用时与lacksPermission 搭配使用
     *
     * @param permission
     *            权限名
     * @return 拥有权限:true,否则false
     */
    public static boolean hasPermission(String permission) {
        return getSubject() != null && permission != null
                && permission.length() > 0
                && getSubject().isPermitted(permission);
    }

    /**
     * 与hasPermission标签逻辑相反,当前用户没有制定权限时,验证通过。
     *
     * @param permission
     *            权限名
     * @return 拥有权限:true,否则false
     */
    public static boolean lacksPermission(String permission) {
        return !hasPermission(permission);
    }

    /**
     * 已认证通过的用户。不包含已记住的用户,这是与user标签的区别所在。与notAuthenticated搭配使用
     *
     * @return 通过身份验证:true,否则false
     */
    public static boolean isAuthenticated() {
        return getSubject() != null && getSubject().isAuthenticated();
    }

    /**
     * 未认证通过用户,与authenticated标签相对应。与guest标签的区别是,该标签包含已记住用户。。
     *
     * @return 没有通过身份验证:true,否则false
     */
    public static boolean notAuthenticated() {
        return !isAuthenticated();
    }

    /**
     * 认证通过或已记住的用户。与guset搭配使用。
     *
     * @return 用户:true,否则 false
     */
    public static boolean isUser() {
        return getSubject() != null && getSubject().getPrincipal() != null;
    }

    /**
     * 验证当前用户是否为“访客”,即未认证(包含未记住)的用户。用user搭配使用
     *
     * @return 访客:true,否则false
     */
    public static boolean isGuest() {
        return !isUser();
    }

    /**
     * 输出当前用户信息,通常为登录帐号信息。
     *
     * @return 当前用户信息
     */
    public static String principal() {
        if (getSubject() != null) {
            Object principal = getSubject().getPrincipal();
            return principal.toString();
        }
        return "";
    }

}

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值