[Spring Boot实战系列] - No.2 Spring boot 整合Spring Security用户管理和用户权限管理

最近,在实习中老师布置一个任务使用spring boot实现 用户管理和用户权限管理。在这里记录一下,以便日后学习。

网站流程图如下:


代码结构如下:


主要结构功能如下:

1.config:实现Spring Security的配置,定义用户角色权限、登录拦截以及相关url对应权限拦截

2.controller:定义控制层、路由处理

3.dao:定义数据层,访问

4.model:定义数据库存储实体类,使用Hibernate实现ORM映射

5.service:定义实现了UserDetails接口的实体类访问服务


Spring Boot 相比之前用过得SSH、SpringMVC + Spring + Hibernate,更加的轻量级,配置很少,包括前端控制器等均不需要。只有一个application.properties配置文件。言归正传、上代码!

首先,定义我们用到的两个实体类:分别为User 和Role,用于实现用户类和角色类:

User.java:

package com.adc.gim.models;


import org.hibernate.validator.constraints.NotEmpty;


import javax.persistence.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Created by YanMing on 2017/5/17.
 */
@Entity
@Table(name="myuser")
public class User implements  UserDetails{

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "email")
    @NotNull
    //@NotEmpty(message="邮箱不能为空")
    private String email;

    //@NotEmpty(message="用户名不能为空")
    @Column(name = "username")
    @NotNull
    private String username;

    //@NotEmpty(message="密码不能为空")
    @Column(name = "password")
    @NotNull
    private String password;

    @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
    private List<Role> roles;

    public User(){

    }

    public User(String email, String name,String password) {
        this.email = email;
        this.username = name;
        this.password = password;
    }

    // Getter and setter methods

    public Long getId() {
        return id;
    }

    public void setId(long value) {
        this.id = value;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String value) {
        this.email = value;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String value) {
        this.username = value;
    }

    public String getPassword() {
        return password;
    }


    public void setPassword(String password) {
        this.password = password;
    }

    // -------------->

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        List<GrantedAuthority> auths = new ArrayList<>();
        List<Role> roles = this.getRoles();
        for (Role role : roles) {
            auths.add(new SimpleGrantedAuthority(role.getRolename()));
        }
        return auths;
    }

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

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

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

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


    public List<Role> getRoles() {
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }
} 

Role.java:

package com.adc.gim.models;

/**
 * Created by YanMing on 2017/5/18.
 */


import javax.persistence.*;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
@Table(name="myrole")
public class Role {

    @Id
    @GeneratedValue
    private Long id;
    private String rolename;


    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getRolename() {
        return rolename;
    }

    public void setRolename(String rolename) {
        this.rolename = rolename;
    }
}

在这里,有几个要讲解的地方:

1.用户和角色是多对多的关系,所以在User里面的roles,我们需要写上@ManyToMany 定义cascade类型

2.实现UserDetails接口:

在Spring Security中,定义了UserDetailsService,这个接口使一个DAO接口,主要用于加载用户数据。所以我们的用户类需要继承UserDetails。这样的话,就会通过Spring Security,在我们登录时访问到数据库,然后比对密码、权限等。详细这部分内容见What is a UserDetailsService and do I need one?

接下来就是DAO层继承了JpaRepository的UserDao用于访问数据库

UserDao.java:

package com.adc.gim.dao;

import com.adc.gim.models.User;
import org.springframework.data.jpa.repository.JpaRepository;

import javax.transaction.Transactional;

/**
 * Created by YanMing on 2017/5/17.
 */
@Transactional
public interface UserDao extends JpaRepository<User,Long>{
    public User findByEmail(String email);
    public User findByUsername(String name);

}
Service层继承UserDetailsService

package com.adc.gim.service;

import com.adc.gim.dao.UserDao;
import com.adc.gim.models.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * Created by YanMing on 2017/5/18.
 */
public class CustomUserService implements UserDetailsService {
    @Autowired
    UserDao userRepository;
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(s

        );
        if (user == null) {
            throw new UsernameNotFoundException("用户名不存在");
        }
        System.out.println("s:"+s);
        System.out.println("username:"+user.getUsername()+";password:"+user.getPassword());
        return user;
    }
}

接下来,就是要配置我们的Spring Security:

package com.adc.gim.config;

import com.adc.gim.service.CustomUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

/**
 * Created by Deng Fuping on 2017/5/18.
 */
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    UserDetailsService userService(){
        return new CustomUserService();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/home", "/login", "/register","/create", "/css/**", "/js/**", "/images/**", "/fonts/**").permitAll()
                .antMatchers("/admin").hasRole("ADMIN")
                .antMatchers("/cmnuser").access("hasRole('ROLE_USER') or hasRole('ROLE_ADMIN')")
                .anyRequest().authenticated()
                .and().exceptionHandling().accessDeniedPage("/accessDenied")
                .and()
                .formLogin()
                .loginPage("/login")
                .defaultSuccessUrl("/loginIn", true)
                .permitAll()
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login")
                .permitAll();
    }

    /*@Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }*/
}
定义了用户角色的权限对应的url访问。设置登录url,登出url等信息,以及匹配特定的url对应用户角色。这部分都很简单。

需要解释的地方是:

.loginPage("/login")
这个的意思是,会将所有需要登录的url访问全部都定向到..../login(GET),然后再Controller中使用RequestMapping,将该url定向到登录界面。设置登录按钮对应提交url为 ..../login(POST),这样Spring Security会通过前面提到的UserDetails加载你的用户信息,然后进行密码校验。如果密码正确,定向到登录成功界面。

同时,对应的.../logout会自动调用内部的登出函数,清除Session,实现登出。

Controller.java这部分比较简单:

package com.adc.gim.controllers;

import com.adc.gim.models.Role;
import com.adc.gim.models.User;
import com.adc.gim.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by YanMing on 2017/5/17.
 */
@Controller
@RequestMapping("/")
public class MainController {

    @Autowired
    private UserDao userDao;

    private List<Role> createUserRole(){
        List<Role> tmpList =  new ArrayList<Role>();
        Role newRole = new Role();
        Long newId = 2L;
        newRole.setId(newId);
        newRole.setRolename("ROLE_USER");
        tmpList.add(newRole);
        return tmpList;
    }

    private List<Role> createAdminRole(){
        List<Role> tmpList =  new ArrayList<Role>();
        Role newRole = new Role();
        Long newId = 1L;
        newRole.setId(newId);
        newRole.setRolename("ROLE_ADMIN");
        tmpList.add(newRole);
        return tmpList;
    }

    @RequestMapping(value = {"/home","/"})
    public String homePage() {
        return "home";
    }

    @RequestMapping(value = "/register")
    public ModelAndView registerPage() {
        ModelAndView model = new ModelAndView();
        User etpUser = new User();
        model.addObject(etpUser);
        //model.setViewName("registerPage");
        model.setViewName("signup");
        return model;
    }

    @RequestMapping(value = "/login",method = RequestMethod.GET)
    public ModelAndView loginPage() {
        ModelAndView model = new ModelAndView();
        User etpUser = new User();
        model.addObject(etpUser);
        //model.setViewName("loginPage");
        model.setViewName("signin");
        return model;
    }

    @RequestMapping(value = "/create",method = RequestMethod.GET)
    public ModelAndView registerUser(@ModelAttribute(value = "user") User user, BindingResult result) {
        ModelAndView model = new ModelAndView();
        if (result.hasErrors()) {
            List<ObjectError> list = result.getAllErrors();
            for(ObjectError error:list){
                System.out.println(error.getCode()+"---"+error.getArguments().toString()+"---"+error.getDefaultMessage());
            }
            model.setViewName("signup");
            model.addObject(user);
        } else {
        try {
            System.out.print(user.getUsername() + user.getEmail()+user.getPassword());
            List<Role> tmpList =  createUserRole();
            user.setRoles(tmpList);
            userDao.save(user);
            //model.setViewName("registerOK");
            model.setViewName("signin");
            model.addObject(user);

        } catch (Exception ex) {
            System.out.print(ex.toString());
            model.setViewName("failPage");
            }
        }
        return model;
    }

    @RequestMapping(value = "/loginIn",method = RequestMethod.GET)
    public ModelAndView loginPost(){
        ModelAndView model = new ModelAndView();
        UserDetails userDetails = (UserDetails) SecurityContextHolder.getContext()
                .getAuthentication()
                .getPrincipal();

        String curName = userDetails.getUsername();
        //System.out.println(curName);
        User user = userDao.findByUsername(curName);
        if(user == null){
            model.setViewName("failPage");
        }else {
            model.addObject(user);
            //Role role = new Role();
            //model.addObject(role);
            //model.setViewName("loginOk");
            model.setViewName("workPage");
        }
        return model;
    }


    @RequestMapping(value = "/fail")
    public ModelAndView failPage() {
        ModelAndView model = new ModelAndView();
        model.setViewName("failPage");
        return model;
    }

    @RequestMapping(value = "/accessDenied")
    public ModelAndView accessDndPage() {
        ModelAndView model = new ModelAndView();
        model.setViewName("accessDenied");
        return model;
    }

    @RequestMapping(value = "regByAdmin",method = RequestMethod.GET)
    @ResponseBody
    public String regByAdmin(@RequestParam(value = "regName",required = false) String regName,
                                   @RequestParam(value = "regPwd",required = false) String regPwd,
                                   @RequestParam(value = "regEml",required = false) String regEml,
                                   @RequestParam(value = "regRole",required = false) String regRole) {
       ModelAndView model = new ModelAndView();
       User newUser = new User(regEml,regName,regPwd);
       String res = "wrong";
       if(regRole.equals("role_user")){
           List<Role> tmpList = createUserRole();
           newUser.setRoles(tmpList);
       }else {
           List<Role> tmpList = createAdminRole();
           newUser.setRoles(tmpList);
       }
       try {
           userDao.save(newUser);
           res = "right";
       }catch (Exception ex){

       }
       return  res;
    }


}

在这里只贴出一张html的文件供参考,其余的可在项目中查看。

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"/>
    <link rel="shortcut icon" href="images/favicon.ico" />
    <link rel="bookmark" href="images/favicon.ico" />
    <title>注册</title>
    <link href="css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
    <link href="css/font-awesome.min.css" rel="stylesheet" type="text/css"/>
</head>
<body>

<h1 style="text-align: center">用户注册</h1>
<br/><br/>
<div class="container">
    <form th:action="@{/create}" th:object="${user}" method="get" th:method="get">

        <div class="input-group">
            <div class="input-group-addon"><span class="glyphicon glyphicon-user"></span> </div>
            <input type="text" id="email" name="email" class="form-control" th:text="${user.email}" placeholder="邮箱"/>

        </div>
        <br/>
        <div class="input-group">
            <div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span> </div>
            <input type="text" id="username" name="username" class="form-control" th:text="${user.username}" placeholder="用户名"/>
        </div>
        <br/>
        <div class="input-group">
            <div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span> </div>
            <input type="password" id="password" name="password" class="form-control" th:text="${user.password}" placeholder="密 码"/>
        </div>
        <br/>
        <input type="submit" class="form-control btn btn-success" value="注 册"/>
    </form>

    <br/>
    <p style="text-align: left" id="hint">已注册?<a href="/login">快去登录</a><br/><br/></p>
</div>





<script src="js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<script>
    /*$('username').blur(function () {
        var username = $('#username').val();
        $.get("/findByName",username,function () {
            
        })
    })*/
</script>
</body>
</html>
Thymeleaf部分可到Thymeleaf官网查看

最后,通过Spring boot的启动器启动,网站假设到此结束。所有的网站架设完毕。运行如下:

主页:


登录界面:


管理员用户显示界面:


普通用户显示界面:


访问拒绝界面:



前端界面来自:萍哥

首页动画来自:天哥


参考文章:江南一点雨

本文GitHub地址


P.S.文章不妥之处还望指正











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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值