1Vue + Jwt + SpringBoot + Ldap 完成登录认证

思路说完了,上代码
1.首先你需要一个Ldap,我使用的是AD。这里我建立了一个叫minibox.com的域,并且添加了一个Employees的OU,其中有2个子OU,子OU中创建了2个用户。

  Ldap结构

  在Groups中新建一些组,把之前创建的用户加入到组中,这样用户就拥有了角色。

  用户组

2.搭建SpringBoot环境
2.1pom文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>minibox</groupId>
  <artifactId>an</artifactId>
  <version>0.0.1-SNAPSHOT</version>
    <!-- Inherit defaults from Spring Boot -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.1.RELEASE</version>
    </parent>
    <!-- Add typical dependencies for a web application -->
    <dependencies>
        <!-- MVC -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- Spring boot test -->
        <dependency>  
            <groupId>org.springframework.boot</groupId>  
            <artifactId>spring-boot-starter-test</artifactId>  
            <scope>test</scope>  
        </dependency>  
        <!-- spring-boot-starter-hateoas -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-hateoas</artifactId>
        </dependency>
        <!-- 热启动 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- JWT -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>
        <!-- Spring Ldap -->
        <dependency>
            <groupId>org.springframework.ldap</groupId>
            <artifactId>spring-ldap-core</artifactId>
            <version>2.3.1.RELEASE</version>
        </dependency>
        <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.24</version>
        </dependency>
    </dependencies>
    <!-- Package as an executable jar -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <!-- Hot swapping -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>springloaded</artifactId>
                        <version>1.2.0.RELEASE</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
</project>

2.2应用配置文件

#Logging_config
logging.level.root=INFO
logging.level.org.springframework.web=WARN
logging.file=minibox.log

#server_config
#使用了SSL,并且在ldap配置中使用了ldaps,这里同时也需要把AD的证书导入到server.keystore中。具体的可以查看java的keytool工具
server.port=8443
server.ssl.key-store=classpath:server.keystore
server.ssl.key-store-password=minibox
server.ssl.key-password=minibox

#jwt
#jwt加解密时使用的key
jwt.key=minibox

#ldap_config
#ldap配置信息,注意这里的userDn一定要写这种形式。referral设置为follow,说不清用途,似乎只有连接AD时才需要配置
ldap.url=ldaps://192.168.227.128:636
ldap.base=ou=Employees,dc=minibox,dc=com
ldap.userDn=cn=Administrator,cn=Users,dc=minibox,dc=com
ldap.userPwd=qqq111!!!!
ldap.referral=follow
ldap.domainName=@minibox.com

3.spring主配置类

package an;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;


@SpringBootApplication//相当于@Configuration,@EnableAutoConfiguration,@ComponentScan
public class Application {

    /*
    * SpringLdap配置。通过@Value注解读取之前配置文件中的值
    */
    @Value("${ldap.url}")
    private String ldapUrl;

    @Value("${ldap.base}")
    private String ldapBase;

    @Value("${ldap.userDn}")
    private String ldapUserDn;

    @Value("${ldap.userPwd}")
    private String ldapUserPwd;

    @Value("${ldap.referral}")
    private String ldapReferral;

    /*
    *SpringLdap的javaConfig注入方式
    */
    @Bean
    public LdapTemplate ldapTemplate() {
        return new LdapTemplate(contextSourceTarget());
    }

    @Bean
    public LdapContextSource contextSourceTarget() {
        LdapContextSource ldapContextSource = new LdapContextSource();
        ldapContextSource.setUrl(ldapUrl);
        ldapContextSource.setBase(ldapBase);
        ldapContextSource.setUserDn(ldapUserDn);
        ldapContextSource.setPassword(ldapUserPwd);
        ldapContextSource.setReferral(ldapReferral);
        return ldapContextSource;
    }


    public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
    }
}

3.1提供认证服务的类

package an.auth;

import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ldap.NamingException;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import an.entity.Employee;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

@RestController
@RequestMapping("/auth")
public class JwtAuth {

    //jwt加密密匙
    @Value("${jwt.key}")
    private String jwtKey;

    //域名后缀
    @Value("${ldap.domainName}")
    private String ldapDomainName;

    //ldap模板
    @Autowired
    private LdapTemplate ldapTemplate;

    /**
     * 将域用户属性通过EmployeeAttributesMapper填充到Employee类中,返回一个填充信息的Employee实例
     */
    private class EmployeeAttributesMapper implements AttributesMapper<Employee> {
        public Employee mapFromAttributes(Attributes attrs) throws NamingException, javax.naming.NamingException {
            Employee employee = new Employee();
            employee.setName((String) attrs.get("sAMAccountName").get());
            employee.setDisplayName((String) attrs.get("displayName").get());
            employee.setRole((String) attrs.get("memberOf").toString());
            return employee;
        }
    }

    /**
     * @param username  用户提交的名称
     * @param password  用户提交的密码
     * @return  成功返回加密后的token信息,失败返回错误HTTP状态码
     */
    @CrossOrigin//因为需要跨域访问,所以要加这个注解
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<String> authByAd(
            @RequestParam(value = "username") String username,
            @RequestParam(value = "password") String password) {
        //这里注意用户名加域名后缀  userDn格式:anwx@minibox.com
        String userDn = username + ldapDomainName;
        //token过期时间 4小时
        Date tokenExpired = new Date(new Date().getTime() + 60*60*4*1000);
        DirContext ctx = null;
        try {
            //使用用户名、密码验证域用户
            ctx = ldapTemplate.getContextSource().getContext(userDn, password);
            //如果验证成功根据sAMAccountName属性查询用户名和用户所属的组
            Employee employee = ldapTemplate                                                        .search(query().where("objectclass").is("person").and("sAMAccountName").is(username),
                            new EmployeeAttributesMapper())
                    .get(0);
            //使用Jwt加密用户名和用户所属组信息
            String compactJws = Jwts.builder()
                    .setSubject(employee.getName())
                    .setAudience(employee.getRole())
                    .setExpiration(tokenExpired)
                    .signWith(SignatureAlgorithm.HS512, jwtKey).compact();
            //登录成功,返回客户端token信息。这里只加密了用户名和用户角色,而displayName和tokenExpired没有加密
            Map<String, Object> userInfo = new HashMap<String, Object>();
            userInfo.put("token", compactJws);
            userInfo.put("displayName", employee.getDisplayName());
            userInfo.put("tokenExpired", tokenExpired.getTime());
            return new ResponseEntity<String>(JSON.toJSONString(userInfo , SerializerFeature.DisableCircularReferenceDetect) , HttpStatus.OK);
        } catch (Exception e) {
            //登录失败,返回失败HTTP状态码
            return new ResponseEntity<String>(HttpStatus.UNAUTHORIZED);
        } finally {
            //关闭ldap连接
            LdapUtils.closeContext(ctx);
        }
    }

}
4.前端Vue
4.1使用Vue-cli搭建项目,并使用vue-router和vue-resource,不了解的可以搜索下
4.2 main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueResource from 'vue-resource'
import store from './store/store'
import 'bootstrap/dist/css/bootstrap.css'
import App from './App'
import Login from './components/login'
import Hello from './components/hello'

Vue.use(VueRouter)
Vue.use(VueResource)
//Vue-resource默认以payload方式提交数据,这样设置之后以formData方式提交
Vue.http.options.emulateJSON = true;

const routes = [
  {
    path: '/login',
    component : Login
  },{
    path: '/hello',
    component: Hello
  }
]

const router = new VueRouter({
  routes
})

//默认导航到登录页
router.push('/login')

/*
全局路由钩子
访问资源时需要验证localStorage中是否存在token
以及token是否过期
验证成功可以继续跳转
失败返回登录页重新登录
 */
router.beforeEach((to, from, next) => {
  if(localStorage.token && new Date().getTime() < localStorage.tokenExpired){
    next()
  }
  else{
    next('/login')
  }
})

new Vue({
  el: '#app',
  template: '<App/>',
  components: { App },
  router,
  store
})
4.3 App.vue
<template>
  <div id="app">
    <router-view></router-view>
  </div>
</template>

<script>
  export default {
    name: 'app',
  }
</script>

<style scoped>
</style>
4.4 login.vue
<template>
    <div class="login-box">
        <div class="login-logo">
            <b>Admin</b>LTE
        </div>
        <div class="login-box-body">
            <div class="input-group form-group has-feedback">
                <span class="input-group-addon"><span class="glyphicon glyphicon-user"></span></span>
                <input v-model="username" type="text" class="form-control" placeholder="username">
                <span class="input-group-addon">@minibox.com</span>
            </div>
            <div class="input-group form-group has-feedback">
                <span class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></span>
                <input v-model="password" type="password" class="form-control" placeholder="Password">
            </div>
            <div class="row">
                <div class="col-sm-6 col-sm-offset-3 col-md-6 col-md-offset-3">
                    <transition name="slide-fade">
                        <p v-if="show">用户名或密码错误</p>
                    </transition>
                </div>
            </div>
            <div class="row">
                <div class="col-sm-6 col-sm-offset-3 col-md-6 col-md-offset-3">
                    <button v-on:click="auth" class="btn btn-primary btn-block btn-flat">Sign In</button>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    //提供认证服务的restApi
    var authUrl = 'https://192.168.227.1:8443/auth'
    export default {
        name: 'app',
        data() {
            return {
                username: '',
                password: '',
                show: false
            }
        },
        methods: {
            auth: function(){
                var credentials = {
                    username:this.username,
                    password:this.password
                }
                /*
                post方法提交username和password
                认证成功将返回的用户信息写入到localStorage,并跳转到下一页面
                失败提示认证错误
                */
                this.$http.post(authUrl, credentials).then(response => {
                    localStorage.token = response.data.token
                    localStorage.tokenExpired = response.data.tokenExpired
                    localStorage.userDisplayName = response.data.displayName
                    this.$router.push('hello')
                }, response => {
                    this.show = true
                })
            }
        }
    }
</script>

<style scoped>
    p{
        text-align: center
    }
    .slide-fade-enter-active {
        transition: all .8s ease;
    }
    .slide-fade-leave-active {
        transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
    }
    .slide-fade-enter, .slide-fade-leave-to
    /* .slide-fade-leave-active for <2.1.8 */ {
        transform: translateX(10px);
        opacity: 0;
    }
    @import '../assets/css/AdminLTE.min.css'
</style>
5效果
5.1访问http://localhost:8000时被导航到登录页

转到登录页

5.2提交登录信息并取得token,跳转下一页

认证成功跳转

到这里整个功能就完成了。本人也是菜鸟一枚,理解有错误的地方还请各位老师指正。打算把整个分布式系统的开发过程记录下来。
参考资源链接:[基于SpringBoot+Vue+MySQL的农产品直卖平台开发与设计](https://wenku.csdn.net/doc/26txtb9i0p?utm_source=wenku_answer2doc_content) 在设计一个农产品直卖平台时,用户身份验证及权限控制是确保平台安全性的核心部分。通过《基于SpringBoot+Vue+MySQL的农产品直卖平台开发与设计》这本书,你可以学习到如何使用Spring Security来实现这一功能。首先,你需要配置Spring Boot应用程序中的Spring Security依赖,并创建用户认证和授权的相关配置。Spring Security框架提供了多种身份验证机制,如表单登录LDAP登录等,你也可以根据需要实现自定义的登录方式。例如,在Spring Boot的配置类中启用并配置Spring Security,通过实现UserDetailsService接口来加载用户信息,并定义密码加密器确保数据安全。在用户成功认证后,你可以利用Spring Security提供的权限管理机制,例如@PreAuthorize注解,来控制用户对于不同功能模块的访问权限。另外,Vue.js前端可以与后端API配合,通过发送携带JWT(JSON Web Tokens)的HTTP请求来实现无状态的认证JWT中可以包含用户的角色信息,后端通过解析JWT验证请求,并根据角色信息返回相应的数据或执行相应操作。确保安全性和用户权限的正确管理对于农产品直卖平台来说至关重要,这不仅保护了平台的稳定运行,也保障了用户数据的安全性。为了更深入地理解用户身份验证和权限控制在实际开发中的应用,我建议阅读《基于SpringBoot+Vue+MySQL的农产品直卖平台开发与设计》这本书,它不仅提供了理论知识,还通过实例演示了具体的实现方法,让你能够获得从概念到实践的全面了解。 参考资源链接:[基于SpringBoot+Vue+MySQL的农产品直卖平台开发与设计](https://wenku.csdn.net/doc/26txtb9i0p?utm_source=wenku_answer2doc_content)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值