当前位置:   article > 正文

SpringSecurity+Vue实现前后端分离的认证授权

SpringSecurity+Vue实现前后端分离的认证授权
一、SpringSecurity的配置

1)pom.xml文件添加依赖

 <!-- spring security依赖 -->
 <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-security</artifactId>
 </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

2)User类,实现了UserDetails

@lombok.Data
public class User implements UserDetails {
    private int id;
    private String username;
    private String account;
    private String password;
    private String email;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date createDate;
    private boolean isLockedOut;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    private Date lastLoginDate;
    private int incorrectLoginAttempt;
    private String realname;
    private String empId;
    private int roleId;
    private boolean isHintEnabled;
    private List<GrantedAuthority> authorities = new ArrayList<>();

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

3)UserMapper接口

@Mapper
public interface UserMapper extends BaseMapper<User> {
    public User findUserByUsername(String account);
    public User findUserByUsernameAndPassword(String account, String password);
    /**查询当前用户拥有的权限*/
    public List<Permission> findPermissionByUsername(String account);
    public List<User> findAllMembersOfCorporation(Integer corporationId);
    public boolean insertOfReturnId(User user);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

4)Mybatis的xml文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.taobao.rigel.rap.mapper.UserMapper">
	<resultMap id="UserMap" type="com.taobao.rigel.rap.model.User">
		<id column="id" property="id"/>
		<result column="name" property="username"/>
		<result column="account" property="account"/>
		<result column="password" property="password"/>
		<result column="email" property="email"/>
		<result column="create_date" property="createDate"/>
		<result column="is_locked_out" property="isLockedOut"/>
		<result column="last_login_date" property="lastLoginDate"/>
		<result column="incorrect_login_attempt" property="incorrectLoginAttempt"/>
		<result column="realname" property="realname"/>
		<result column="emp_id" property="empId"/>
		<result column="is_hint_enabled" property="isHintEnabled"/>
	</resultMap>
	<select id="findUserByUsername" parameterType="java.lang.String" resultMap="UserMap">
		select u.* , r.role from tb_user_new u left join tb_role_and_user_new ur on u.id = ur.user_id left join tb_role_new r on ur.role_id = r.id where u.account=#{account}
	</select>

	<select id="findPermissionByUsername" parameterType="String" resultType="com.taobao.rigel.rap.model.Permission">
        SELECT permission.* FROM tb_user_new user
            INNER JOIN tb_role_and_user_new user_role ON user.id = user_role.user_id
            INNER JOIN tb_permission_and_role_new role_permission ON user_role.role_id = role_permission.role_id
            INNER JOIN tb_permission_new permission ON role_permission.permission_id = permission.id
            WHERE user.account=#{account};
    </select>

	<select id="findAllMembersOfCorporation" parameterType="java.lang.Integer" resultMap="UserMap">
		select u.* from tb_user_new u INNER JOIN tb_corporation_and_user_new cu on u.id = cu.user_id where cu.corporation_id=#{corporationId}
	</select>

	<update id="update" parameterType="com.taobao.rigel.rap.model.User">
		update tb_user_new set name=#{username}, account=#{account}, password=#{password}, email=#{email}, last_login_date=#{lastLoginDate} where id=#{id}
	</update>

	<insert id="insertOfReturnId" parameterType="com.taobao.rigel.rap.model.User" useGeneratedKeys="true" keyProperty="id">
		insert into tb_user_new (name, account, password, email, create_date, last_login_date) values (#{username}, #{account}, #{password}, #{email}, #{createDate}, #{lastLoginDate})
		<selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
			select LAST_INSERT_ID()
		</selectKey>
	</insert>
	
	<delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
		delete from tb_user_new where id=#{id}
	</delete>
</mapper>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

5)自定义UserDetailsService接口的实现类

@Service
public class MyUserDetailService implements UserDetailsService {

    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        System.out.println("loadUserByUsername");
        //根据用户名查询用户信息
        User user = userMapper.findUserByUsername(account);
        System.out.println("从数据库中查询出的用户信息:" + user);
        if(user != null){
            //根据用户名查询当前用户所有权限
            List<Permission> permissionList = userMapper.findPermissionByUsername(account);
            //authorities: 存放所有用户权限
            List<GrantedAuthority> authorities = new ArrayList<>();
            for(Permission permission : permissionList){
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permission.getPermission());
                authorities.add(grantedAuthority);
            }
            //把所有权限赋值给user
            user.setAuthorities(authorities);
            logger.info("当前用户为: "+user);
        }else{
            System.out.println("用户不存在");
//            throw new UsernameNotFoundException("用户不存在!");
        }
        return user;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

6)自定义WebSecurityConfigurerAdapter的配置类

@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
    Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    MyUserDetailService myUserDetailService;

    //Control+O 打开重写方法
    //定制请求的授权规则
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        System.out.println("进入到 MySecurityConfig的configure 方法中");
        //super.configure(http);
        /*Spring Security 的默认构造器:
        通过调用authorizeRequests()和 anyRequest().authenticated()就会要求所有进入应用的HTTP请求都要进行认证
        http
            .authorizeRequests()
                .anyRequest().authenticated()
                .and()
            .formLogin().and()
            .httpBasic(); //弹出一个输入用户名、密码的登录框

            
             “/shop/hello” 和 “/shop/order” 这两个路径必须进过认证,并且 “/shop/order” 必须是 post 请求的方式.
             对于其他的请求,我们都是 .anyRequest().permitAll() ;都放行.
             http.authorizeRequests()
                .antMatchers("/shop/hello").authenticated()
                .antMatchers(HttpMethod.POST,"/shop/order").authenticated()
                .anyRequest().permitAll();

             antMatchers()方法所使用的路径可能会包括Ant风格的通配符,而regexMatchers()方法则能够接受正则表达式来定义请求路径。
         */
        // 基于token,所以不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // /**代表所有的请求
        http.authorizeRequests()//方法表示开启了认证规则配置;定义哪些url需要保护,哪些url不需要保护;
                .antMatchers("/api/**").permitAll()//定义不需要认证就可以访问
//                .antMatchers("/session/**").permitAll()//定义不需要认证就可以访问
//                .antMatchers("/component/**").hasAuthority("ROLE_ADMIN")
                .antMatchers("/home/**").hasAnyAuthority("ROLE_DEV_APPLICATION","ROLE_ADMIN","ROLE_DEV_TERMINAL_IOS")
                .anyRequest().authenticated();其他的路径都是登录后即可访问
//                .and().formLogin().loginPage("/")
//
//                //在successHandler中,使用response返回登录成功的json即可,切记不可以使用defaultSuccessUrl,defaultSuccessUrl是只登录成功后重定向的页面,failureHandler也是由于相同的原因不使用failureUrl。
//                .loginProcessingUrl("/login").successHandler(
//                        new AuthenticationSuccessHandler(){
//                            @Override
//                            public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
//                                System.out.println(authentication.getDetails());
//                                httpServletResponse.setContentType("application/json;charset=utf-8");
//                                PrintWriter out = httpServletResponse.getWriter();
//                                out.write("{\"status\":\"success\",\"msg\":\"登录成功\"}");
//                                out.flush();
//                                out.close();
//                            }
//                }).failureHandler(
//                        new AuthenticationFailureHandler() {
//                            @Override
//                            public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
//                                httpServletResponse.setContentType("application/json;charset=utf-8");
//                                PrintWriter out = httpServletResponse.getWriter();
//                                out.write("{\"status\":\"failed\",\"msg\":\"登录失败\"}");
//                                out.flush();
//                                out.close();
//                            }
//                });

                //http.logout()开启自动配置的注销功能
                //1) 访问/logout 表示用户注销,清空session
                //2) 注销成功会返回/login?logout 页面
                //3) logoutSuccessfulUrl 改变2)的设置
                http.logout().logoutSuccessUrl("/login");

                http.sessionManagement().invalidSessionUrl("/login");

                http.rememberMe().rememberMeParameter("remember");
//                .usernameParameter("username").passwordParameter("password").defaultSuccessUrl("/");定义当需要用户登录时候,转到的登录页面
//                http.headers().frameOptions().disable();


//                .antMatchers("/level2/**").hasRole("VIP2")
//                .antMatchers("/level3/**").hasRole("VIP3");

        //开启自动配置的登录功能。如果没有登录,没有权限就会来到登录页面
        //1:/login来到登录页
        //2:重定向/login?error表示登录失败
        //3:更多详细规定
        //http.formLogin().defaultSuccessUrl("/user/login.html");

        /* iframe */
	    http.headers().frameOptions().sameOrigin(); // 运行同一个域名中的任何请求
        http.csrf().disable(); // 默认是启用的,需要禁用CSRF保护(当不使用cookie时可以禁用csrf)
        http.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class);
        // 禁用缓存
        http.headers().cacheControl();
    }

    //定制请求的认证规则
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        System.out.println("进入到 configureGlobal 方法中");
        System.out.println("auth.userDetailsService(myUserDetailService):" + auth.userDetailsService(myUserDetailService));
    //1)获取内存中的用户名和密码
//        auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
                .withUser("1").password(new BCryptPasswordEncoder().encode("1")).roles("USER");
//                .withUser("1").password(new BCryptPasswordEncoder().encode("1")).authorities("ROLE_TEST");

//        auth.authenticationProvider(authenticationProvider());
//        auth.userDetailsService(myUserDetailService).passwordEncoder(passwordEncoder());

    //2)获取数据库中的用户名和密码
        auth.userDetailsService(myUserDetailService).passwordEncoder(new PasswordEncoder() {
            @Override
            public String encode(CharSequence charSequence) {
                return charSequence.toString();
            }

            @Override
            public boolean matches(CharSequence charSequence, String s) {
                return s.equals(charSequence.toString());
            }
        });
    }

    /*
     通过AuthenticationProvider方式获取
     */
    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        System.out.println("进入到 authenticationProvider 方法中");
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(myUserDetailService);
        authProvider.setPasswordEncoder(passwordEncoder());
        return authProvider;
    }

    /**
     * 密码生成策略
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        System.out.println("进入到 passwordEncoder 方法中");
        return new BCryptPasswordEncoder();
    }

    @Bean
    public JwtAuthenticationTokenFilter authenticationTokenFilterBean() throws Exception {
        System.out.println("进入到 authenticationTokenFilterBean 方法中");
        return new JwtAuthenticationTokenFilter();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152

7)LoginController

@RestController
@RequestMapping(value = "api/umeapiplus/login")
public class LoginController {
    @Autowired
    MyUserDetailService myUserDetailService;

    @Autowired
    JwtTokenUtils jwtTokenUtils;

    @Autowired
    BCryptPasswordEncoder bCryptPasswordEncoder;
    
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Map<String, Object> login(@RequestBody JSONObject requestParam){
        System.out.println("前端传递过来的登陆信息参数:" + JSONObject.toJSONString(requestParam));
        Map<String, Object> returnMap = new HashMap<>();
        String account = requestParam.getString("account");
        String password = requestParam.getString("password");
        User user = (User) myUserDetailService.loadUserByUsername(account);
        System.out.println("返回的用户信息:" + user);
        if(user == null){
            System.out.println("账号不存在");
            returnMap.put("code", 400);
            returnMap.put("msg", "账号不存在");
            returnMap.put("data", null);
            returnMap.put("token", null);
        }else {
        	// 正常应该是从user.getPassword()得到数据库中的加密密码
            String encodedPassword = bCryptPasswordEncoder.encode(password);
            System.out.println("原始密码:" + password + " bcr加密后的密码:" + encodedPassword);
            boolean compareRes = bCryptPasswordEncoder.matches(password, encodedPassword);
            System.out.println("compareRes: " + compareRes);
            if(!compareRes){
                System.out.println("密码错误");
                returnMap.put("code", 400);
                returnMap.put("msg", "密码错误");
                returnMap.put("data", null);
                returnMap.put("token", null);
            }else {
                String token = jwtTokenUtils.generateToken(user);
                System.out.println("根据用户名和密码生成的token信息:" + token);
                returnMap.put("code", 200);
                returnMap.put("msg", "登录成功");
                returnMap.put("data", user);
                returnMap.put("token", token);
            }
        }
        return returnMap;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
二、Vue项目代码

1)main.js

import Vue from 'vue'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
import axios from 'axios'
Vue.prototype.axios = axios
import store from './store'
import router from './router'
import App from './App.vue'
import './css/style.css'

Vue.use(Antd);
Vue.config.productionTip = false

router.beforeEach((to, from, next) => {
  console.log('to.path: ' + to.path);
  if (to.path === '/') {
    next();
  } else {
    if (window.sessionStorage.getItem("user")) {
      next();
    }
  }
})

new Vue({
  store,
  router,
  render: h => h(App),
}).$mount('#app')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

2)vue-router的index.js

import Vue from 'vue'
import Router from 'vue-router'
import Login from '../components/login/Login.vue'
import HomePage from '../components/New/HomePage.vue'

Vue.use(Router)

export default new Router({
    base: '/dist/', //添加的地方
    routes: [
        {
            path: '/',
            name: 'Login',
            component: Login,
            hidden: true
        },
        {
            path: '/home',
            name: 'Home',
            component: HomePage,
            hidden: true,
            meta: {
                roles: ['admin', 'user']
            }
        },
        {
            path: '*',
            redirect: '/home'
        }
    ]
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

3)Login.vue

<template>
    <div>
        <a-form
                :rules="rules"
                :form="form"
                ref="loginForm"
                :model="loginForm"
                @submit="submitLogin"
                class="loginContainer">
            <h3 class="loginTitle">系统登录</h3>
            <span style="font-size: 8px;color: #13c2c2">用户名为邮箱前缀,密码为123456</span>
            <a-form-item label="用户名">
                <a-input type="text" v-model="loginForm.account" auto-complete="off" placeholder="请输入用户名"></a-input>
            </a-form-item>
            <a-form-item label="密码">
                <a-input type="password" v-model="loginForm.password" auto-complete="off" placeholder="请输入密码"></a-input>
            </a-form-item>
            <a-form-item :wrapper-col="{ span: 12, offset: 5 }">
                <a-button type="primary" html-type="submit">
                    登录
                </a-button>
            </a-form-item>
        </a-form>
    </div>
</template>

<script>
    import {login} from '../../utils/api'

    export default {
        name: "Login",
        data() {
            return {
                loading: false,
                loginForm: {
                    account: '',
                    password: ''
                },
                checked: true,
                rules: {
                    account: [{required: true, message: '请输入用户名', trigger: 'blur'}],
                    password: [{required: true, message: '请输入密码', trigger: 'blur'}],
                }
            }
        },
        beforeCreate() {
            this.form = this.$form.createForm(this, {name: 'validate_other'})
        },
        methods: {
            submitLogin(e) {
                e.preventDefault();
                this.form.validateFields((err, values) => {
                    if (!err) {
                        console.log('Received values of form: ', values);
                        this.loading = true;
                        console.log('this.loginForm: ' + JSON.stringify(this.loginForm))
                        login(this.loginForm).then((res) => {
                            this.loading = false;
                            console.log('登录的返回结果:' + JSON.stringify(res.data))
                            this.$message.info(res.data.msg)
                            if (res.data.code === 200) {
                                this.$store.commit('editCurrentUser', res.data.data);
                                window.sessionStorage.setItem('user', JSON.stringify(res.data.data));
                                this.$router.push('home').catch(err => {
                                    console.log('all good ' + err)
                                })
                            }
                        })
                    } else {
                        return false;
                    }
                });
            }
        }
    }
</script>

<style>
    .loginContainer {
        border-radius: 15px;
        background-clip: padding-box;
        margin: 180px auto;
        width: 350px;
        padding: 15px 35px 15px 35px;
        background: #fff;
        border: 1px solid #eaeaea;
        box-shadow: 0 0 25px #cac6c6;
    }

    .loginTitle {
        margin: 15px auto 20px auto;
        text-align: center;
        color: #505458;
    }
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/article/detail/53928
推荐阅读
相关标签
  

闽ICP备14008679号